مقدمة
لذلك، نحن نعلم أن جافا لديها المواضيع. يمكنك أن تقرأ عن ذلك في المراجعة التي تحمل عنوان Better Together: Java and the Thread class. الجزء الأول – خيوط التنفيذ . المواضيع ضرورية لأداء العمل بالتوازي. وهذا يجعل من المحتمل جدًا أن تتفاعل الخيوط بطريقة أو بأخرى مع بعضها البعض. دعونا نلقي نظرة على كيفية حدوث ذلك وما هي الأدوات الأساسية التي لدينا.أَثْمَر
Thread.yield() محير ونادرًا ما يستخدم. يتم وصفه بعدة طرق مختلفة على الإنترنت. بما في ذلك بعض الأشخاص الذين يكتبون أن هناك قائمة انتظار من سلاسل الرسائل، حيث سيتم نزول الخيط بناءً على أولويات الخيط. يكتب أشخاص آخرون أن الخيط سيغير حالته من "قيد التشغيل" إلى "قابل للتشغيل" (على الرغم من عدم وجود تمييز بين هذه الحالات، أي أن Java لا تميز بينهما). والحقيقة هي أن الأمر كله أقل شهرة ولكنه أبسط إلى حد ما. يوجد خطأ ( JDK-6416721: (سلسلة المواصفات) Fix Thread.yield() javadoc ) تم تسجيله فيyield()
وثائق الطريقة. إذا قرأته، فمن الواضح أن هذه yield()
الطريقة تقدم في الواقع بعض التوصيات فقط إلى برنامج جدولة سلاسل رسائل Java، والتي يمكن من خلالها منح هذا الخيط وقتًا أقل للتنفيذ. ولكن ما يحدث بالفعل، أي ما إذا كان المجدول يتصرف بناءً على التوصية وما يفعله بشكل عام، يعتمد على تنفيذ JVM ونظام التشغيل. وقد يعتمد الأمر على بعض العوامل الأخرى أيضًا. يرجع كل الالتباس على الأرجح إلى حقيقة أنه تمت إعادة التفكير في تعدد مؤشرات الترابط مع تطور لغة Java. اقرأ المزيد في النظرة العامة هنا: مقدمة موجزة عن Java Thread.yield()
.
ينام
يمكن أن ينتقل الخيط إلى وضع السكون أثناء تنفيذه. هذا هو أسهل نوع من التفاعل مع المواضيع الأخرى. نظام التشغيل الذي يقوم بتشغيل جهاز Java الظاهري الذي يقوم بتشغيل كود Java الخاص بنا لديه برنامج جدولة سلاسل رسائل خاص به . فهو يقرر أي موضوع سيبدأ ومتى. لا يمكن للمبرمج التفاعل مع هذا المجدول مباشرة من كود Java، فقط من خلال JVM. يمكنه أن يطلب من المجدول إيقاف الخيط مؤقتًا لفترة من الوقت، أي وضعه في وضع السكون. يمكنك قراءة المزيد في هذه المقالات: Thread.sleep() و كيف يعمل Multithreading . يمكنك أيضًا التحقق من كيفية عمل سلاسل الرسائل في أنظمة تشغيل Windows: Internals of Windows Thread . والآن دعونا نرى ذلك بأعيننا. احفظ الكود التالي في ملف اسمهHelloWorldApp.java
:
class HelloWorldApp {
public static void main(String []args) {
Runnable task = () -> {
try {
int secToWait = 1000 * 60;
Thread.currentThread().sleep(secToWait);
System.out.println("Woke up");
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Thread thread = new Thread(task);
thread.start();
}
}
كما ترون، لدينا بعض المهام التي تنتظر لمدة 60 ثانية، وبعد ذلك ينتهي البرنامج. نقوم بالتجميع باستخدام الأمر " javac HelloWorldApp.java
" ثم نقوم بتشغيل البرنامج باستخدام " java HelloWorldApp
". من الأفضل أن تبدأ البرنامج في نافذة منفصلة. على سبيل المثال، في نظام التشغيل Windows، يكون الأمر كما يلي: start java HelloWorldApp
. نستخدم الأمر jps للحصول على PID (معرف العملية)، ونفتح قائمة سلاسل الرسائل باستخدام " jvisualvm --openpid pid
: كما ترون، أصبح مؤشر الترابط الخاص بنا الآن في حالة "السكون". في الواقع، هناك طريقة أكثر أناقة للمساعدة موضوعنا لديه أحلام سعيدة:
try {
TimeUnit.SECONDS.sleep(60);
System.out.println("Woke up");
} catch (InterruptedException e) {
e.printStackTrace();
}
هل لاحظت أننا نتعامل InterruptedException
في كل مكان؟ دعونا نفهم لماذا.
الموضوع. المقاطعة ()
المشكلة هي أنه أثناء انتظار/سكون الخيط، قد يرغب شخص ما في مقاطعته. في هذه الحالة، نحن نتعامل معInterruptedException
. تم إنشاء هذه الآلية بعد Thread.stop()
إعلان أن الطريقة مهملة، أي أنها قديمة وغير مرغوب فيها. كان السبب هو أنه عندما stop()
تم استدعاء الطريقة، تم "قتل" الخيط ببساطة، وهو أمر لا يمكن التنبؤ به على الإطلاق. لم نتمكن من معرفة متى سيتم إيقاف مؤشر الترابط، ولم نتمكن من ضمان اتساق البيانات. تخيل أنك تكتب البيانات إلى ملف أثناء إيقاف مؤشر الترابط. بدلاً من قتل سلسلة المحادثات، قرر منشئو Java أنه سيكون من المنطقي أكثر إخبارها بوجوب مقاطعتها. إن كيفية الرد على هذه المعلومات هي مسألة يقررها الموضوع نفسه. لمزيد من التفاصيل، اقرأ لماذا تم إهمال Thread.stop؟
على موقع أوراكل. لنلقي نظرة على مثال:
public static void main(String []args) {
Runnable task = () -> {
try {
TimeUnit.SECONDS.sleep(60);
} catch (InterruptedException e) {
System.out.println("Interrupted");
}
};
Thread thread = new Thread(task);
thread.start();
thread.interrupt();
}
في هذا المثال، لن ننتظر 60 ثانية. بدلاً من ذلك، سنعرض على الفور "تمت المقاطعة". وذلك لأننا قمنا بتسمية interrupt()
الطريقة الموجودة على الخيط. تقوم هذه الطريقة بتعيين علامة داخلية تسمى "حالة المقاطعة". أي أن كل مؤشر ترابط له علامة داخلية لا يمكن الوصول إليها مباشرة. لكن لدينا طرقًا أصلية للتفاعل مع هذا العلم. لكن هذه ليست الطريقة الوحيدة. قد يكون الخيط قيد التشغيل، ولا ينتظر شيئًا ما، بل يقوم ببساطة بتنفيذ الإجراءات. ولكنها قد تتوقع أن الآخرين سوف يرغبون في إنهاء عملها في وقت محدد. على سبيل المثال:
public static void main(String []args) {
Runnable task = () -> {
while(!Thread.currentThread().isInterrupted()) {
// Do some work
}
System.out.println("Finished");
};
Thread thread = new Thread(task);
thread.start();
thread.interrupt();
}
في المثال أعلاه، while
سيتم تنفيذ الحلقة حتى تتم مقاطعة الخيط خارجيًا. أما بالنسبة للعلم isInterrupted
، فمن المهم أن نعرف أنه إذا التقطنا علامة InterruptedException
، فسيتم إعادة تعيين علامة isInterrupted، ثم isInterrupted()
ستعود خطأ. تحتوي فئة Thread أيضًا على طريقة Thread.interrupted()
ثابتة تنطبق فقط على مؤشر الترابط الحالي، ولكن هذه الطريقة تعيد تعيين العلامة إلى خطأ! اقرأ المزيد في هذا الفصل بعنوان مقاطعة الموضوع
.
انضم (انتظر حتى ينتهي موضوع آخر)
أبسط نوع من الانتظار هو انتظار انتهاء موضوع آخر.public static void main(String []args) throws InterruptedException {
Runnable task = () -> {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
System.out.println("Interrupted");
}
};
Thread thread = new Thread(task);
thread.start();
thread.join();
System.out.println("Finished");
}
في هذا المثال، سوف ينام الخيط الجديد لمدة 5 ثوانٍ. في الوقت نفسه، سينتظر الخيط الرئيسي حتى يستيقظ الخيط النائم وينتهي من عمله. إذا نظرت إلى حالة سلسلة الرسائل في JVisualVM، فستبدو كما يلي: بفضل أدوات المراقبة، يمكنك رؤية ما يحدث مع سلسلة الرسائل. الطريقة join
بسيطة جدًا، لأنها مجرد طريقة تحتوي على كود Java ويتم تنفيذها wait()
طالما أن الخيط الذي يتم الاتصال به على قيد الحياة. بمجرد أن يموت الخيط (عند الانتهاء من عمله)، ينقطع الانتظار. وهذا هو كل سحر الطريقة join()
. لذلك، دعونا ننتقل إلى الشيء الأكثر إثارة للاهتمام.
شاشة
يتضمن تعدد مؤشرات الترابط مفهوم الشاشة. تأتي كلمة "مراقب" إلى اللغة الإنجليزية عن طريق اللغة اللاتينية في القرن السادس عشر وتعني "أداة أو جهاز يستخدم للمراقبة أو التحقق أو الاحتفاظ بسجل مستمر لعملية ما". وفي سياق هذه المقالة، سنحاول تغطية الأساسيات. لمن يريد التفاصيل، يرجى التعمق في المواد المرتبطة. نبدأ رحلتنا مع مواصفات لغة جافا (JLS): 17.1. التزامن . تقول ما يلي: اتضح أن Java تستخدم آلية "مراقبة" للمزامنة بين سلاسل الرسائل. ترتبط الشاشة بكل كائن، ويمكن للخيوط الحصول عليهاlock()
أو تحريرها باستخدام unlock()
. بعد ذلك، سنجد البرنامج التعليمي على موقع Oracle الإلكتروني: Intrinsic Locks and Synchronization
. يقول هذا البرنامج التعليمي أن مزامنة Java مبنية على كيان داخلي يسمى القفل الداخلي أو قفل الشاشة . غالبًا ما يُطلق على هذا القفل اسم " الشاشة ". نرى أيضًا مرة أخرى أن كل كائن في Java له قفل داخلي مرتبط به. يمكنك قراءة Java - الأقفال والمزامنة الجوهرية
. بعد ذلك، سيكون من المهم فهم كيفية ربط كائن في Java بشاشة. في Java، يحتوي كل كائن على رأس يخزن بيانات التعريف الداخلية غير المتاحة للمبرمج من التعليمات البرمجية، ولكن يحتاجها الجهاز الظاهري للعمل بشكل صحيح مع الكائنات. يتضمن رأس الكائن "كلمة علامة"، والتي تبدو كما يلي:
https://edu.netbeans.org/contrib/slides/java-overview-and-java-se6.pdf
public class HelloWorld{
public static void main(String []args){
Object object = new Object();
synchronized(object) {
System.out.println("Hello World");
}
}
}
هنا، يستخدم مؤشر الترابط الحالي (الذي يتم تنفيذ أسطر التعليمات البرمجية عليه) الكلمة synchronized
الأساسية لمحاولة استخدام الشاشة المرتبطة بالمتغير object"\
للحصول على/الحصول على القفل. إذا لم يتنافس أي شخص آخر على الشاشة (أي لا يقوم أي شخص آخر بتشغيل تعليمات برمجية متزامنة باستخدام نفس الكائن)، فقد تحاول Java إجراء تحسين يسمى "القفل المتحيز". تتم إضافة علامة ذات صلة وسجل حول مؤشر الترابط الذي يمتلك قفل الشاشة إلى كلمة العلامة في رأس الكائن. وهذا يقلل من الحمل المطلوب لقفل الشاشة. إذا كانت الشاشة مملوكة سابقًا لخيط آخر، فإن هذا القفل لا يكفي. يتحول JVM إلى النوع التالي من القفل: "القفل الأساسي". ويستخدم عمليات المقارنة والمبادلة (CAS). علاوة على ذلك، لم تعد كلمة علامة رأس الكائن نفسها تخزن كلمة العلامة، بل هي إشارة إلى مكان تخزينها، وتتغير العلامة حتى يفهم JVM أننا نستخدم القفل الأساسي. إذا تنافست عدة سلاسل (تتنافس) على شاشة (أحدها حصل على القفل، والثاني ينتظر تحرير القفل)، فستتغير العلامة الموجودة في كلمة العلامة، وتقوم كلمة العلامة الآن بتخزين مرجع إلى الشاشة ككائن - بعض الكيانات الداخلية لـ JVM. كما هو مذكور في اقتراح تحسين JDK (JEP)، يتطلب هذا الموقف مساحة في منطقة الكومة الأصلية من الذاكرة لتخزين هذا الكيان. سيتم تخزين المرجع إلى موقع ذاكرة هذا الكيان الداخلي في كلمة علامة رأس الكائن. وبالتالي، فإن الشاشة هي في الحقيقة آلية لمزامنة الوصول إلى الموارد المشتركة بين عدة سلاسل عمليات. يقوم JVM بالتبديل بين العديد من تطبيقات هذه الآلية. لذلك، للتبسيط، عندما نتحدث عن الشاشة، فإننا نتحدث في الواقع عن الأقفال.
متزامن (في انتظار القفل)
كما رأينا سابقًا، يرتبط مفهوم "الكتلة المتزامنة" (أو "القسم الحرج") ارتباطًا وثيقًا بمفهوم الشاشة. ألق نظرة على مثال:public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
Runnable task = () -> {
synchronized(lock) {
System.out.println("thread");
}
};
Thread th1 = new Thread(task);
th1.start();
synchronized(lock) {
for (int i = 0; i < 8; i++) {
Thread.currentThread().sleep(1000);
System.out.print(" " + i);
}
System.out.println(" ...");
}
}
هنا، يقوم الخيط الرئيسي أولاً بتمرير كائن المهمة إلى الخيط الجديد، ثم يحصل على القفل على الفور وينفذ عملية طويلة معه (8 ثوانٍ). كل هذا الوقت، المهمة غير قادرة على المتابعة، لأنها لا تستطيع الدخول إلى synchronized
الكتلة، لأن القفل قد تم الحصول عليه بالفعل. إذا لم يتمكن الخيط من الحصول على القفل، فسوف ينتظر الشاشة. بمجرد حصوله على القفل، سيستمر التنفيذ. عندما يخرج الخيط من الشاشة، فإنه يحرر القفل. في JVisualVM، يبدو الأمر كما يلي: كما ترى في JVisualVM، الحالة هي "Monitor"، مما يعني أن الخيط محظور ولا يمكنه التقاط الشاشة. يمكنك أيضًا استخدام التعليمات البرمجية لتحديد حالة سلسلة الرسائل، لكن أسماء الحالة المحددة بهذه الطريقة لا تتطابق مع الأسماء المستخدمة في JVisualVM، على الرغم من تشابهها. في هذه الحالة، th1.getState()
ستعود العبارة الموجودة في حلقة for إلى BLOCKED ، لأنه طالما أن الحلقة قيد التشغيل، فإن lock
شاشة الكائن مشغولة بالخيط main
، th1
ويتم حظر الخيط ولا يمكن المتابعة حتى يتم تحرير القفل. بالإضافة إلى الكتل المتزامنة، يمكن مزامنة الطريقة بأكملها. على سبيل المثال، إليك طريقة من الفصل HashTable
:
public synchronized int size() {
return count;
}
سيتم تنفيذ هذه الطريقة بواسطة مؤشر ترابط واحد فقط في أي وقت محدد. هل نحن حقا بحاجة إلى القفل؟ نعم، نحن في حاجة إليها. في حالة أساليب المثيل، يعمل الكائن "هذا" (الكائن الحالي) كقفل. هناك مناقشة مثيرة للاهتمام حول هذا الموضوع هنا: هل هناك ميزة لاستخدام الطريقة المتزامنة بدلاً من الكتلة المتزامنة؟
. إذا كانت الطريقة ثابتة، فلن يكون القفل هو الكائن "هذا" (نظرًا لعدم وجود كائن "هذا" للطريقة الثابتة)، بل كائن فئة (على سبيل المثال، ) Integer.class
.
انتظر (في انتظار الشاشة). طرق الإخطار () وإخطار الكل ().
تحتوي فئة Thread على طريقة انتظار أخرى مرتبطة بالشاشة. على عكسsleep()
و join()
، لا يمكن استدعاء هذه الطريقة ببساطة. اسمها wait()
. يتم استدعاء هذه wait
الطريقة على الكائن المرتبط بالشاشة الذي نريد انتظاره. دعونا نرى مثالا:
public static void main(String []args) throws InterruptedException {
Object lock = new Object();
// The task object will wait until it is notified via lock
Runnable task = () -> {
synchronized(lock) {
try {
lock.wait();
} catch(InterruptedException e) {
System.out.println("interrupted");
}
}
// After we are notified, we will wait until we can acquire the lock
System.out.println("thread");
};
Thread taskThread = new Thread(task);
taskThread.start();
// We sleep. Then we acquire the lock, notify, and release the lock
Thread.currentThread().sleep(3000);
System.out.println("main");
synchronized(lock) {
lock.notify();
}
}
في JVisualVM، يبدو الأمر كما يلي: لفهم كيفية عمل ذلك، تذكر أن الأساليب wait()
والطرق notify()
مرتبطة بـ java.lang.Object
. قد يبدو من الغريب وجود طرق مرتبطة بالخيط في Object
الفصل. لكن السبب وراء ذلك يتكشف الآن. ستتذكر أن كل كائن في Java له رأس. يحتوي الرأس على معلومات متنوعة عن التدبير المنزلي، بما في ذلك معلومات حول الشاشة، أي حالة القفل. تذكر أن كل كائن، أو مثيل لفئة، يرتبط بكيان داخلي في JVM، يسمى القفل أو الشاشة الجوهرية. في المثال أعلاه، يشير رمز كائن المهمة إلى أننا نقوم بإدخال الكتلة المتزامنة للشاشة المرتبطة بالكائن lock
. إذا نجحنا في الحصول على القفل لهذه الشاشة، فسيتم wait()
استدعاؤه. سيقوم الخيط الذي ينفذ المهمة بتحرير lock
شاشة الكائن، ولكنه سيدخل في قائمة انتظار سلاسل الرسائل التي تنتظر الإشعار من lock
شاشة الكائن. تسمى قائمة الانتظار هذه بـ WAIT SET، وهو ما يعكس الغرض منها بشكل أكثر دقة. أي أنها مجموعة أكثر من كونها قائمة انتظار. يقوم مؤشر الترابط main
بإنشاء مؤشر ترابط جديد مع كائن المهمة، ويبدأه، وينتظر 3 ثوانٍ. وهذا يجعل من المحتمل جدًا أن يتمكن الخيط الجديد من الحصول على القفل قبل الخيط main
، والدخول في قائمة انتظار الشاشة. بعد ذلك، main
يدخل الخيط نفسه إلى lock
الكتلة المتزامنة للكائن ويقوم بإجراء إشعار الخيط باستخدام الشاشة. بعد إرسال الإعلام، main
يقوم الخيط بتحرير lock
شاشة الكائن، ويستمر الخيط الجديد، الذي كان ينتظر سابقًا lock
تحرير شاشة الكائن، في التنفيذ. من الممكن إرسال إشعار إلى مؤشر ترابط واحد فقط ( notify()
) أو في وقت واحد إلى كافة سلاسل الرسائل في قائمة الانتظار ( notifyAll()
). اقرأ المزيد هنا: الفرق بين notify() و notifyAll() في Java
. من المهم ملاحظة أن ترتيب الإشعارات يعتمد على كيفية تنفيذ JVM. اقرأ المزيد هنا: كيفية حل مشكلة المجاعة باستخدام الإشعارات والإخطارات؟
. يمكن إجراء المزامنة دون تحديد كائن. يمكنك القيام بذلك عندما تتم مزامنة الطريقة بأكملها بدلاً من كتلة واحدة من التعليمات البرمجية. على سبيل المثال، بالنسبة للطرق الثابتة، سيكون القفل كائن فئة (يتم الحصول عليه عبر .class
):
public static synchronized void printA() {
System.out.println("A");
}
public static void printB() {
synchronized(HelloWorld.class) {
System.out.println("B");
}
}
فيما يتعلق باستخدام الأقفال، كلا الطريقتين متماثلتان. إذا لم تكن الطريقة ثابتة، فسيتم إجراء المزامنة باستخدام التيار instance
، أي باستخدام this
. بالمناسبة، قلنا سابقًا أنه يمكنك استخدام الطريقة getState()
للحصول على حالة الموضوع. على سبيل المثال، بالنسبة لمؤشر الترابط الموجود في قائمة الانتظار في انتظار جهاز العرض، ستكون الحالة WAITING أو TIMED_WAITING، إذا wait()
حددت الطريقة مهلة.
https://stackoverflow.com/questions/36425942/what-is-the-lifecycle-of-thread-in-Java
دورة حياة الخيط
على مدار حياته، تتغير حالة الخيط. في الواقع، تشمل هذه التغييرات دورة حياة الخيط. بمجرد إنشاء الموضوع، تصبح حالته جديدة. في هذه الحالة، لا يتم تشغيل مؤشر الترابط الجديد بعد، ولا يعرف برنامج جدولة سلاسل رسائل Java أي شيء عنه بعد. لكي يتعرف برنامج جدولة الخيط على الخيط، يجب عليك استدعاء الأسلوبthread.start()
. ثم سينتقل الخيط إلى حالة RUNNABLE. يحتوي الإنترنت على الكثير من المخططات غير الصحيحة التي تميز بين الحالات "القابلة للتشغيل" و"الجارية". ولكن هذا خطأ، لأن Java لا تميز بين "جاهز للعمل" (قابل للتشغيل) و"العمل" (قيد التشغيل). عندما يكون الخيط حيًا ولكنه غير نشط (غير قابل للتشغيل)، فإنه يكون في إحدى الحالتين:
- محظور - في انتظار دخول قسم حرج، أي
synchronized
كتلة. - الانتظار - انتظار مؤشر ترابط آخر لتلبية بعض الشروط.
getState()
الطريقة. المواضيع لديها أيضًا isAlive()
طريقة تُرجع صحيحًا إذا لم يتم إنهاء الموضوع.
LockSupport ومواقف السيارات الموضوع
بدءًا من Java 1.6، ظهرت آلية مثيرة للاهتمام تسمى LockSupport . تقوم هذه الفئة بربط "التصريح" بكل مؤشر ترابط يستخدمه. يعود استدعاء الأسلوبpark()
فورًا إذا كان التصريح متاحًا، مما يؤدي إلى استهلاك التصريح في العملية. وإلا فإنه يمنع. يؤدي استدعاء unpark
الطريقة إلى جعل التصريح متاحًا إذا لم يكن متاحًا بعد. لا يوجد سوى تصريح واحد. تشير وثائق Java LockSupport
إلى Semaphore
الفئة. دعونا نلقي نظرة على مثال بسيط:
import java.util.concurrent.Semaphore;
public class HelloWorldApp{
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(0);
try {
semaphore.acquire();
} catch (InterruptedException e) {
// Request the permit and wait until we get it
e.printStackTrace();
}
System.out.println("Hello, World!");
}
}
سينتظر هذا الرمز دائمًا، لأن الإشارة الآن لديها 0 تصاريح. وعندما acquire()
يتم استدعاء الكود (أي طلب التصريح)، ينتظر الخيط حتى يتلقى التصريح. وبما أننا ننتظر، يجب علينا التعامل مع InterruptedException
. ومن المثير للاهتمام أن الإشارة تحصل على حالة خيط منفصلة. إذا نظرنا إلى JVisualVM، فسنرى أن الحالة ليست "انتظر"، بل "بارك". دعونا ننظر إلى مثال آخر:
public static void main(String[] args) throws InterruptedException {
Runnable task = () -> {
// Park the current thread
System.err.println("Will be Parked");
LockSupport.park();
// As soon as we are unparked, we will start to act
System.err.println("Unparked");
};
Thread th = new Thread(task);
th.start();
Thread.currentThread().sleep(2000);
System.err.println("Thread state: " + th.getState());
LockSupport.unpark(th);
Thread.currentThread().sleep(2000);
}
ستكون حالة مؤشر الترابط في انتظار، لكن JVisualVM يميز بين wait
الكلمة synchronized
الأساسية park
والفئة LockSupport
. لماذا هذا LockSupport
بغاية الأهمية؟ نعود مرة أخرى إلى وثائق Java وننظر إلى حالة مؤشر ترابط الانتظار
. كما ترون، هناك ثلاث طرق فقط للوصول إليها. اثنان من هذه الطرق هما wait()
و join()
. والثالث هو LockSupport
. في Java، يمكن أيضًا إنشاء الأقفال على LockSuppor
t وتقديم أدوات ذات مستوى أعلى. دعونا نحاول استخدام واحد. على سبيل المثال، ألق نظرة على ReentrantLock
:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class HelloWorld{
public static void main(String []args) throws InterruptedException {
Lock lock = new ReentrantLock();
Runnable task = () -> {
lock.lock();
System.out.println("Thread");
lock.unlock();
};
lock.lock();
Thread th = new Thread(task);
th.start();
System.out.println("main");
Thread.currentThread().sleep(2000);
lock.unlock();
}
}
كما هو الحال في الأمثلة السابقة، كل شيء بسيط هنا. ينتظر الكائن lock
أن يقوم شخص ما بتحرير المورد المشترك. إذا نظرنا إلى JVisualVM، فسنرى أن الخيط الجديد سيتم إيقافه حتى main
يقوم الخيط بتحرير القفل إليه. يمكنك قراءة المزيد حول الأقفال هنا: Java 8 StampedLocks vs. ReadWriteLocks وSynchronized
and Lock API في Java. لفهم كيفية تنفيذ الأقفال بشكل أفضل، من المفيد أن تقرأ عن Phaser في هذه المقالة: دليل Java Phaser
. وبالحديث عن المزامنات المختلفة، يجب عليك قراءة مقالة
DZone على The Java Synchronizers.
خاتمة
في هذه المراجعة، قمنا بفحص الطرق الرئيسية التي تتفاعل بها سلاسل الرسائل في Java. مواد اضافية:- أفضل معًا: Java وفئة Thread. الجزء الأول – خيوط التنفيذ
- https://dzone.com/articles/the-java-synchronizers
- https://www.javatpoint.com/java-multithreading-interview-questions
GO TO FULL VERSION