JavaRush /مدونة جافا /Random-AR /أهم 50 سؤالاً وأجوبة في مقابلات جافا الأساسية. الجزء 3
Roman Beekeeper
مستوى

أهم 50 سؤالاً وأجوبة في مقابلات جافا الأساسية. الجزء 3

نشرت في المجموعة
أهم 50 سؤالاً وأجوبة في مقابلات جافا الأساسية. الجزء الأول: أهم 50 سؤالاً وأجوبة في مقابلات جافا الأساسية. الجزء 2

تعدد الخيوط

37. كيفية إنشاء موضوع جديد (تدفق) في جافا؟

بطريقة أو بأخرى، يتم الإنشاء من خلال استخدام فئة Thread. ولكن قد تكون هناك خيارات هنا ...
  1. نحن نرث منjava.lang.Thread
  2. نقوم بتنفيذ واجهة java.lang.Runnableيقبل كائنها Threadفئة المُنشئ
دعونا نتحدث عن كل واحد منهم.

نحن نرث من فئة الموضوع

لإنجاز هذا العمل، في فصلنا نرث من java.lang.Thread. إنه يحتوي على الميثامفيتامين run()، وهو بالضبط ما نحتاجه. ستكون كل حياة ومنطق الخيط الجديد بهذه الطريقة. هذا نوع من mainالطريقة لموضوع جديد. بعد ذلك، كل ما تبقى هو إنشاء كائن من فئتنا وتنفيذ الطريقة start()، والتي ستقوم بإنشاء موضوع جديد وتشغيل المنطق المكتوب فيه. دعونا ننظر:
/**
* Пример того, How создавать треды путем наследования {@link Thread} класса.
*/
class ThreadInheritance extends Thread {

   @Override
   public void run() {
       System.out.println(Thread.currentThread().getName());
   }

   public static void main(String[] args) {
       ThreadInheritance threadInheritance1 = new ThreadInheritance();
       ThreadInheritance threadInheritance2 = new ThreadInheritance();
       ThreadInheritance threadInheritance3 = new ThreadInheritance();
       threadInheritance1.start();
       threadInheritance2.start();
       threadInheritance3.start();
   }
}
سيكون الإخراج إلى وحدة التحكم مثل هذا:

Thread-1
Thread-0
Thread-2
وهذا هو، حتى هنا نرى أن المواضيع لا يتم تنفيذها بدورها، ولكن كما قرر JVM)

تنفيذ واجهة Runnable

إذا كنت ضد الميراث و/أو ورثت بالفعل إحدى الفئات الأخرى، فيمكنك استخدام java.lang.Runnable. هنا في صفنا نقوم بتنفيذ هذه الواجهة وتنفيذ الطريقة run()، كما كان في هذا المثال. تحتاج فقط إلى إنشاء المزيد من الكائنات Thread. يبدو أن المزيد من الخطوط أسوأ. لكننا نعرف مدى ضرر الميراث وأنه من الأفضل تجنبه بكل الوسائل ;) لننظر:
/**
* Пример того, How создавать треды из интерфейса {@link Runnable}.
* Здесь проще простого - реализуем этот интерфейс и потом передаем в конструктор
* экземпляр реализуемого an object.
*/
class ThreadInheritance implements Runnable {

   @Override
   public void run() {
       System.out.println(Thread.currentThread().getName());
   }

   public static void main(String[] args) {
       ThreadInheritance runnable1 = new ThreadInheritance();
       ThreadInheritance runnable2 = new ThreadInheritance();
       ThreadInheritance runnable3 = new ThreadInheritance();

       Thread threadRunnable1 = new Thread(runnable1);
       Thread threadRunnable2 = new Thread(runnable2);
       Thread threadRunnable3 = new Thread(runnable3);

       threadRunnable1.start();
       threadRunnable2.start();
       threadRunnable3.start();
   }
}
ونتيجة التنفيذ:

Thread-0
Thread-1
Thread-2

38. ما الفرق بين العملية والخيط؟

أهم 50 سؤالاً وأجوبة في مقابلات جافا الأساسية.  الجزء 3 - 1هناك الاختلافات التالية بين العملية والخيط:
  1. يُسمى البرنامج قيد التنفيذ عملية، بينما يُسمى الخيط مجموعة فرعية من العملية.
  2. العمليات مستقلة، في حين أن المواضيع هي مجموعة فرعية من العملية.
  3. تحتوي العمليات على مساحة عنوان مختلفة في الذاكرة، بينما تحتوي الخيوط على مساحة عنوان مشتركة.
  4. يعد تبديل السياق أسرع بين سلاسل العمليات مقارنة بالعمليات.
  5. يعد الاتصال بين العمليات أبطأ وأكثر تكلفة من الاتصال بين الخيوط.
  6. أي تغييرات في العملية الأصل لا تؤثر على العملية الفرعية، في حين أن التغييرات في الخيط الأصل يمكن أن تؤثر على الخيط الفرعي.

39. ما هي مزايا تعدد الخيوط؟

أهم 50 سؤالاً وأجوبة في مقابلات جافا الأساسية.  الجزء 3 - 2
  1. يتيح تعدد مؤشرات الترابط للتطبيق/البرنامج الاستجابة دائمًا للإدخال حتى لو كان يقوم بالفعل بتشغيل بعض المهام في الخلفية؛
  2. يتيح لك Multithreading إكمال المهام بشكل أسرع لأن سلاسل الرسائل تنفذ بشكل مستقل؛
  3. يوفر تعدد مؤشرات الترابط استخدامًا أفضل لذاكرة التخزين المؤقت لأن سلاسل العمليات تشترك في موارد الذاكرة المشتركة؛
  4. يؤدي تعدد مؤشرات الترابط إلى تقليل مقدار الخادم المطلوب لأن خادمًا واحدًا يمكنه تشغيل عدة سلاسل رسائل في وقت واحد.

40. ما هي الحالات في دورة حياة الخيط؟

أهم 50 سؤالاً وأجوبة في مقابلات جافا الأساسية.  الجزء 3 - 3
  1. جديد: في هذه الحالة، يتم إنشاء كائن فئة Threadباستخدام عامل التشغيل الجديد، ولكن مؤشر الترابط غير موجود. لا يبدأ الخيط حتى نستدعي start().
  2. قابل للتشغيل: في هذه الحالة، يكون مؤشر الترابط جاهزًا للتشغيل بعد استدعاء الطريقة يبدأ(). ومع ذلك، لم يتم تحديده بعد بواسطة برنامج جدولة سلسلة المحادثات.
  3. قيد التشغيل: في هذه الحالة، يقوم برنامج جدولة الخيط بتحديد خيط من حالة الاستعداد وتشغيله.
  4. انتظار/محظور: في هذه الحالة، لا يتم تشغيل مؤشر الترابط ولكنه لا يزال نشطًا أو في انتظار اكتمال مؤشر ترابط آخر.
  5. ميت/منته: عند خروج الطريقة، run()يكون مؤشر الترابط في حالة منتهية أو ميتة.

41. هل من الممكن بدء موضوع مرتين؟

لا، لا يمكننا إعادة تشغيل الخيط لأنه بمجرد بدء تشغيل الخيط وتنفيذه، فإنه ينتقل إلى الحالة الميتة. لذا، إذا حاولنا تشغيل مؤشر الترابط مرتين، فسيقوم بطرح استثناء وقت التشغيل " java.lang.IllegalThreadStateException ". دعونا ننظر:
class DoubleStartThreadExample extends Thread {

   /**
    * Имитируем работу треда
    */
   public void run() {
	// что-то происходит. Для нас не существенно на этом этапе
   }

   /**
    * Запускаем тред дважды
    */
   public static void main(String[] args) {
       DoubleStartThreadExample doubleStartThreadExample = new DoubleStartThreadExample();
       doubleStartThreadExample.start();
       doubleStartThreadExample.start();
   }
}
بمجرد أن يصل العمل إلى البداية الثانية لنفس الخيط، سيكون هناك استثناء. جرب ذلك بنفسك؛) من الأفضل أن ترى مرة واحدة بدلاً من أن تسمع مائة مرة.

42. ماذا لو قمت باستدعاء طريقة التشغيل () مباشرة دون استدعاء طريقة البداية ()؟

نعم، run()بالطبع يمكنك استدعاء إحدى الطرق، لكن هذا لن يؤدي إلى إنشاء سلسلة رسائل جديدة وتنفيذها كسلسلة رسائل منفصلة. في هذه الحالة، هو كائن بسيط يستدعي طريقة بسيطة. أما إذا كنا نتحدث عن الطريقة start()فالأمر مختلف. بإطلاق هذه الطريقة، runtimeتطلق طريقة جديدة، وتقوم بدورها بتشغيل طريقتنا؛) إذا كنت لا تصدقني، جربها:
class ThreadCallRunExample extends Thread {

   public void run() {
       for (int i = 0; i < 5; i++) {
           System.out.print(i);
       }
   }

   public static void main(String args[]) {
       ThreadCallRunExample runExample1 = new ThreadCallRunExample();
       ThreadCallRunExample runExample2 = new ThreadCallRunExample();

       // просто будут вызваны в потоке main два метода, один за другим.
       runExample1.run();
       runExample2.run();
   }
}
وسيكون الإخراج إلى وحدة التحكم كما يلي:

0123401234
يمكن ملاحظة أنه لم يتم إنشاء أي موضوع. كل شيء يعمل مثل الطبقة العادية. في البداية نجحت الطريقة الأولى، ثم الثانية.

43. ما هو الخيط الخفي؟

أهم 50 سؤالاً وأجوبة في مقابلات جافا الأساسية.  الجزء 3 - 4الخيط الخفي (المشار إليه فيما بعد باسم الخيط الخفي) هو خيط يؤدي مهام في الخلفية فيما يتعلق بخيط آخر. أي أن وظيفتها هي أداء المهام المساعدة التي يجب القيام بها فقط مع مؤشر ترابط (رئيسي) آخر. هناك العديد من سلاسل العمليات التي تعمل تلقائيًا، مثل Garbage Collector وFinalizer وما إلى ذلك.

لماذا تغلق Java مؤشر الترابط الخفي؟

الغرض الوحيد من مؤشر الترابط الخفي هو توفير الخدمات لمؤشر ترابط المستخدم لمهمة دعم الخلفية. لذلك، إذا اكتمل مؤشر الترابط الرئيسي، فسيقوم وقت التشغيل تلقائيًا بإغلاق كافة سلاسل الرسائل الخاصة به.

طرق العمل في فئة الموضوع

يوفر الفصل java.lang.Threadطريقتين للعمل مع البرنامج الخفي للخيط:
  1. public void setDaemon(boolean status)- يشير إلى أن هذا سيكون خيطًا خفيًا. الإعداد الافتراضي هو false، مما يعني أنه سيتم إنشاء سلاسل رسائل غير خفية ما لم يتم تحديدها بشكل منفصل.
  2. public boolean isDaemon()- هذا في الأساس عبارة عن أداة getter للمتغير daemonالذي قمنا بتعيينه باستخدام الطريقة السابقة.
مثال:
class DaemonThreadExample extends Thread {

   public void run() {
       // Проверяет, демон ли этот поток or нет
       if (Thread.currentThread().isDaemon()) {
           System.out.println("daemon thread");
       } else {
           System.out.println("user thread");
       }
   }

   public static void main(String[] args) {
       DaemonThreadExample thread1 = new DaemonThreadExample();
       DaemonThreadExample thread2 = new DaemonThreadExample();
       DaemonThreadExample thread3 = new DaemonThreadExample();

       // теперь thread1 - поток-демон.
       thread1.setDaemon(true);

       System.out.println("демон?.. " + thread1.isDaemon());
       System.out.println("демон?.. " + thread2.isDaemon());
       System.out.println("демон?.. " + thread3.isDaemon());

       thread1.start();
       thread2.start();
       thread3.start();
   }
}
إخراج وحدة التحكم:

демон?.. true
демон?.. false
демон?.. false
daemon thread
user thread
user thread
من الإخراج نرى أنه داخل الخيط نفسه، باستخدام currentThread()طريقة ثابتة، يمكننا معرفة أي خيط هو من ناحية، من ناحية أخرى، إذا كان لدينا إشارة إلى كائن هذا الخيط، فيمكننا معرفة ذلك منه مباشرة. وهذا يعطي المرونة اللازمة في التكوين.

44. هل من الممكن جعل الخيط خفيًا بعد إنشائه؟

لا. إذا قمت بذلك فإنه سيتم طرح استثناء IllegalThreadStateException. لذلك، لا يمكننا إنشاء خيط خفي إلا قبل أن يبدأ. مثال:
class SetDaemonAfterStartExample extends Thread {

   public void run() {
       System.out.println("Working...");
   }

   public static void main(String[] args) {
       SetDaemonAfterStartExample afterStartExample = new SetDaemonAfterStartExample();
       afterStartExample.start();

       // здесь будет выброшено исключение
       afterStartExample.setDaemon(true);
   }
}
إخراج وحدة التحكم:

Working...
Exception in thread "main" java.lang.IllegalThreadStateException
	at java.lang.Thread.setDaemon(Thread.java:1359)
	at SetDaemonAfterStartExample.main(SetDaemonAfterStartExample.java:14)

45. ما هو خطاف الاغلاق؟

Shutdownhook هو سلسلة رسائل يتم استدعاؤها ضمنيًا قبل إيقاف تشغيل JVM (Java Virtual Machine). لذلك يمكننا استخدامه لتنظيف أحد الموارد أو حفظ الحالة عندما يتم إيقاف تشغيل Java Virtual Machine بشكل طبيعي أو فجأة. يمكننا الإضافة shutdown hookباستخدام الطريقة التالية:
Runtime.getRuntime().addShutdownHook(new ShutdownHookThreadExample());
كما هو موضح في المثال:
/**
* Программа, которая показывает How запустить shutdown hook тред,
* который выполнится аккурат до окончания работы JVM
*/
class ShutdownHookThreadExample extends Thread {

   public void run() {
       System.out.println("shutdown hook задачу выполнил");
   }

   public static void main(String[] args) {

       Runtime.getRuntime().addShutdownHook(new ShutdownHookThreadExample());

       System.out.println("Теперь программа засыпает, нажмите ctrl+c чтоб завершить ее.");
       try {
           Thread.sleep(60000);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
   }
}
إخراج وحدة التحكم:

Теперь программа засыпает, нажмите ctrl+c чтоб завершить ее.
shutdown hook задачу выполнил

46. ​​ما هو التزامن؟

المزامنة في Java هي القدرة على التحكم في وصول سلاسل رسائل متعددة إلى أي مورد مشترك. عندما تحاول عدة سلاسل تنفيذ نفس المهمة، هناك احتمال الحصول على نتيجة خاطئة، لذا للتغلب على هذه المشكلة، تستخدم Java المزامنة، والتي يمكن أن يعمل من خلالها مؤشر ترابط واحد فقط في المرة الواحدة. يمكن تحقيق المزامنة بثلاث طرق:
  • طريقة المزامنة
  • عن طريق مزامنة كتلة معينة
  • مزامنة ثابتة

مزامنة الطريقة

يتم استخدام الطريقة المتزامنة لتأمين كائن لأي مورد مشترك. عندما يستدعي خيط طريقة متزامنة، فإنه يحصل تلقائيًا على قفل على هذا الكائن ويحرره عندما يكمل الخيط مهمته. ولإنجاحه، تحتاج إلى إضافة الكلمة الأساسية المتزامنة . دعونا نرى كيف يعمل هذا مع مثال:
/**
* Пример, где мы синхронизируем метод. То есть добавляем ему слово synchronized.
* Есть два писателя, которые хотят использовать один принтер. Они подготовor свои поэмы
* И конечно же не хотят, чтоб их поэмы перемешались, а хотят, чтоб работа была сделана по * * * очереди для каждого из них
*/
class Printer {

   synchronized void print(List<String> wordsToPrint) {
       wordsToPrint.forEach(System.out::print);
       System.out.println();
   }

   public static void main(String args[]) {
       // один an object для двух тредов
       Printer printer  = new Printer();

       // создаем два треда
       Writer1 writer1 = new Writer1(printer);
       Writer2 writer2 = new Writer2(printer);

       // запускаем их
       writer1.start();
       writer2.start();
   }
}

/**
* Писатель номер 1, который пишет свою поэму.
*/
class Writer1 extends Thread {
   Printer printer;

   Writer1(Printer printer) {
       this.printer = printer;
   }

   public void run() {
       List<string> poem = Arrays.asList("Я ", this.getName(), " Пишу", " Письмо");
       printer.print(poem);
   }

}

/**
* Писатель номер 2, который пишет свою поэму.
*/
class Writer2 extends Thread {
   Printer printer;

   Writer2(Printer printer) {
       this.printer = printer;
   }

   public void run() {
       List<String> poem = Arrays.asList("Не Я ", this.getName(), " Не пишу", " Не Письмо");
       printer.print(poem);
   }
}
والإخراج إلى وحدة التحكم:

Я Thread-0 Пишу Письмо
Не Я Thread-1 Не пишу Не Письмо

كتلة المزامنة

يمكن استخدام الكتلة المتزامنة لإجراء المزامنة على أي مورد أسلوب محدد. لنفترض أنه بطريقة كبيرة (نعم، نعم، لا يمكنك كتابة مثل هذه الأشياء، ولكن في بعض الأحيان يحدث ذلك) تحتاج إلى مزامنة جزء صغير فقط، لسبب ما. إذا قمت بوضع جميع رموز الطريقة في كتلة متزامنة، فستعمل بنفس طريقة الطريقة المتزامنة. بناء الجملة يبدو مثل هذا:
synchronized (“an object для блокировки”) {
   // сам code, который нужно защитить
}
لكي لا نكرر المثال السابق، سنقوم بإنشاء سلاسل رسائل من خلال فئات مجهولة - أي تنفيذ الواجهة القابلة للتشغيل على الفور.
/**
* Вот How добавляется блок синхронизации.
* Внутри нужно указать у кого будет взят мьютекс для блокировки.
*/
class Printer {

   void print(List<String> wordsToPrint) {
       synchronized (this) {
           wordsToPrint.forEach(System.out::print);
       }
       System.out.println();
   }

   public static void main(String args[]) {
       // один an object для двух тредов
       Printer printer = new Printer();

       // создаем два треда
       Thread writer1 = new Thread(new Runnable() {
           @Override
           public void run() {
               List<String> poem = Arrays.asList("Я ", "Writer1", " Пишу", " Письмо");
               printer.print(poem);
           }
       });
       Thread writer2 = new Thread(new Runnable() {
           @Override
           public void run() {
               List<String> poem = Arrays.asList("Не Я ", "Writer2", " Не пишу", " Не Письмо");
               printer.print(poem);
           }
       });

       // запускаем их
       writer1.start();
       writer2.start();
   }
}

}
والإخراج إلى وحدة التحكم

Я Writer1 Пишу Письмо
Не Я Writer2 Не пишу Не Письмо

مزامنة ثابتة

إذا قمت بمزامنة طريقة ثابتة، فسيكون القفل على الفئة، وليس على الكائن. في هذا المثال، نقوم بتطبيق الكلمة الأساسية المتزامنة على طريقة ثابتة لإجراء المزامنة الثابتة:
/**
* Вот How добавляется блок синхронизации.
* Внутри нужно указать у кого будет взят мьютекс для блокировки.
*/
class Printer {

   static synchronized void print(List<String> wordsToPrint) {
       wordsToPrint.forEach(System.out::print);
       System.out.println();
   }

   public static void main(String args[]) {

       // создаем два треда
       Thread writer1 = new Thread(new Runnable() {
           @Override
           public void run() {
               List<String> poem = Arrays.asList("Я ", "Writer1", " Пишу", " Письмо");
               Printer.print(poem);
           }
       });
       Thread writer2 = new Thread(new Runnable() {
           @Override
           public void run() {
               List<String> poem = Arrays.asList("Не Я ", "Writer2", " Не пишу", " Не Письмо");
               Printer.print(poem);
           }
       });

       // запускаем их
       writer1.start();
       writer2.start();
   }
}
والإخراج إلى وحدة التحكم:

Не Я Writer2 Не пишу Не Письмо
Я Writer1 Пишу Письмо

47. ما هو المتغير المتقلب؟

يتم استخدام الكلمة الأساسية volatileفي البرمجة متعددة الخيوط لتوفير أمان الخيط نظرًا لأن التعديل على متغير واحد قابل للتغيير يكون مرئيًا لجميع سلاسل العمليات الأخرى، لذلك يمكن استخدام متغير واحد بواسطة خيط واحد في المرة الواحدة. باستخدام الكلمة الأساسية، volatileيمكنك ضمان أن المتغير سيكون آمنًا لمؤشر الترابط وسيتم تخزينه في الذاكرة المشتركة، ولن تأخذه سلاسل الرسائل إلى ذاكرة التخزين المؤقت الخاصة بها. كيف تبدو؟
private volatile AtomicInteger count;
نضيف فقط إلى المتغير volatile. ولكن هذا لا يعني السلامة الكاملة للخيط. بعد كل شيء، قد لا تكون العمليات ذرية على متغير. ولكن يمكنك استخدام Atomicالفئات التي تؤدي العملية ذريًا، أي في تنفيذ واحد بواسطة المعالج. يمكن العثور على العديد من هذه الفئات في الحزمة java.util.concurrent.atomic.

48. ما هو الجمود

يعد الجمود في Java جزءًا من تعدد العمليات. يمكن أن يحدث حالة توقف تام عندما يكون الخيط ينتظر قفل الكائن الذي حصل عليه خيط آخر، وينتظر الخيط الثاني قفل الكائن الذي حصل عليه الخيط الأول. وبالتالي، ينتظر هذان الخيطان بعضهما البعض ولن يستمرا في تنفيذ التعليمات البرمجية الخاصة بهما. أهم 50 سؤالاً وأجوبة في مقابلات جافا الأساسية.  الجزء 3 - 5دعونا نلقي نظرة على مثال يوجد فيه فئة تقوم بتنفيذ Runnable. يقبل اثنين من الموارد في منشئه. داخل طريقة run()، يتم أخذ القفل لهم واحدًا تلو الآخر، لذلك إذا قمت بإنشاء كائنين من هذه الفئة ونقل الموارد بترتيب مختلف، يمكنك بسهولة تشغيل القفل:
class DeadLock {

   public static void main(String[] args) {
       final Integer r1 = 10;
       final Integer r2 = 15;

       DeadlockThread threadR1R2 = new DeadlockThread(r1, r2);
       DeadlockThread threadR2R1 = new DeadlockThread(r2, r1);

       new Thread(threadR1R2).start();
       new Thread(threadR2R1).start();
   }
}

/**
* Класс, который принимает два ресурса.
*/
class DeadlockThread implements Runnable {

   private final Integer r1;
   private final Integer r2;

   public DeadlockThread(Integer r1, Integer r2) {
       this.r1 = r1;
       this.r2 = r2;
   }

   @Override
   public void run() {
       synchronized (r1) {
           System.out.println(Thread.currentThread().getName() + " захватил ресурс: " + r1);

           try {
               Thread.sleep(1000);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }

           synchronized (r2) {
               System.out.println(Thread.currentThread().getName() + " захватил ресурс: " + r2);
           }
       }
   }
}
إخراج وحدة التحكم:

Первый тред захватил первый ресурс
Второй тред захватывает второй ресурс

49. كيفية تجنب الجمود؟

وبناء على ما نعرفه عن كيفية حدوث الجمود، يمكننا استخلاص بعض الاستنتاجات...
  • كما هو موضح في المثال أعلاه، كان الجمود بسبب تداخل الأقفال. أي أن داخل القفل الواحد يوجد آخر أو أكثر. يمكنك تجنب ذلك بالطريقة التالية - بدلاً من التداخل، تحتاج إلى إضافة تجريد جديد في الأعلى وإعطاء القفل إلى مستوى أعلى، وإزالة الأقفال المتداخلة.
  • كلما زاد الحظر، زادت فرصة الوصول إلى طريق مسدود. لذلك، في كل مرة تقوم فيها بإضافة قفل، عليك أن تفكر فيما إذا كانت هناك حاجة إليه حقًا وما إذا كان من الممكن تجنب إضافة قفل جديد.
  • الاستخدامات Thread.join(). يمكن أيضًا حدوث حالة توقف تام عندما يكون هناك مؤشر ترابط واحد في انتظار آخر. لتجنب هذه المشكلة، قد تفكر في تعيين حد زمني للأسلوب join().
  • إذا كان لدينا موضوع واحد، فلن يكون هناك طريق مسدود ;)

50. ما هي حالة السباق؟

إذا كانت السيارات تؤدي أداءً في السباقات الحقيقية، ففي مصطلحات السباق الخاصة بالخيوط المتعددة، تؤدي الخيوط أداءً في السباقات. لكن لماذا؟ هناك خيطان قيد التشغيل ويمكنهما الوصول إلى نفس الكائن. ويمكنهم محاولة تحديث الحالة في نفس الوقت. حتى الآن كل شيء واضح، أليس كذلك؟ وبالتالي فإن الخيوط تعمل إما بالتوازي الحقيقي (إذا كان هناك أكثر من نواة واحدة في المعالج) أو بالتوازي المشروط عندما يخصص المعالج فترة زمنية قصيرة. ولا يمكننا التحكم في هذه العمليات، لذلك لا يمكننا ضمان أنه عندما يقرأ مؤشر ترابط واحد البيانات من كائن ما، سيكون لديه الوقت لتغييرها قبل أن يقوم مؤشر ترابط آخر بذلك. تحدث مثل هذه المشكلات عندما تمر مجموعة الاختبار والفعل هذه. ماذا يعني ذلك؟ على سبيل المثال، لدينا ifتعبير في النص يتغير الشرط نفسه، وهو:
int z = 0;

// проверь
if (z < 5) {
//действуй
   z = z + 5;
}
لذلك قد يكون هناك موقف عندما يدخل خيطان في نفس الوقت في كتلة التعليمات البرمجية هذه في الوقت الذي لا تزال فيه z مساوية للصفر ويقومان معًا بتغيير هذه القيمة. وفي النهاية لن نحصل على القيمة المتوقعة 5 بل 10. كيف نتجنب ذلك؟ تحتاج إلى القفل قبل التنفيذ وبعده. أي أنه لكي يدخل الخيط الأول إلى الكتلة if، قم بتنفيذ جميع الإجراءات وتغييرها، zوعندها فقط امنح الخيط التالي الفرصة للقيام بذلك. لكن الخيط التالي لن يدخل إلى الكتلة if، لأنه zسيكون بالفعل يساوي 5:
// получить блокировку для z
if (z < 5) {
   z = z + 5;
}
// выпустить из блокировки z
===================================================

بدلا من الإخراج

أريد أن أقول شكرا لكل من قرأ حتى النهاية. لقد كانت رحلة طويلة وقمت بها! قد لا يكون كل شيء واضحا. هذا جيد. بمجرد أن بدأت في تعلم Java، لم أتمكن من فهم ماهية المتغير الثابت. لكن لا شيء، لقد نمت مع هذا الفكر، وقرأت بعض المصادر الأخرى وفهمت أخيرًا. يعد التحضير للمقابلة مسألة أكاديمية أكثر من كونها عملية. لذلك، قبل كل مقابلة، تحتاج إلى تكرار وتحديث ذاكرتك بالأشياء التي قد لا تستخدمها كثيرًا.

وكما هو الحال دائمًا، روابط مفيدة:

شكرًا لكم جميعًا على القراءة، نراكم قريبًا) ملفي الشخصي على GitHub
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION