قبل Java 5.0 ، تم مزامنة الآليات الوحيدة التي يمكن استخدامها لتنسيق الوصول إلى الكائنات المشتركة والمتقلب. نحن نعلم أن الكلمة الرئيسية المتزامنة تنفذ أقفال مدمجة ، في حين أن الكلمة الرئيسية المتطايرة تضمن رؤية الذاكرة للتصوير المتعدد. في معظم الحالات ، يمكن لهذه الآليات القيام بالمهمة بشكل جيد ، لكن لا يمكنها تنفيذ بعض الوظائف الأكثر تقدماً ، مثل عدم قدرتها على مقاطعة خيط في انتظار الحصول على قفل ، وعدم القدرة على تنفيذ آلية قفل محدودة زمنيا ، وعدم القدرة على تنفيذ قواعد القفل للهياكل غير الحظر ، وما إلى ذلك ، وعادة ما توفر آليات الاقتران الأكثر مرونة نشاطًا أو أداء أفضل. لذلك ، تمت إضافة آلية جديدة في Java 5.0: REENTRANTLOCK. تقوم فئة REENTRANTLOCK بتنفيذ واجهة القفل وتوفر نفس رؤية MUTEX والذاكرة كما تزامن. طبقتها الأساسية هي تحقيق التزامن متعدد الخيوط من خلال AQS. بالمقارنة مع الأقفال المدمجة ، لا يوفر REENTRANTLOCK آلية قفل أكثر ثراءً فحسب ، بل لا توفر أيضًا أدنى من الأقفال المدمجة في الأداء (حتى أفضل من الأقفال المدمجة في الإصدارات السابقة). بعد أن تحدثت عن العديد من مزايا Reentrantlock ، دعنا نكتشف رمز المصدر ونرى تنفيذها المحدد.
1. مقدمة في الكلمات الرئيسية المتزامنة
توفر Java أقفال مدمجة لدعم التزامن متعدد الخيوط. يحدد JVM كتلة الكود المتزامن وفقًا للكلمة الرئيسية المتزامنة. عندما يدخل مؤشر ترابط كتلة الكود المتزامن ، فإنه سيحصل تلقائيًا على القفل. عند الخروج من كتلة التعليمات البرمجية المتزامنة ، سيتم إصدار القفل تلقائيًا. بعد الحصول على مؤشر ترابط واحد ، سيتم حظر مؤشرات الترابط الأخرى. يمكن استخدام كل كائن java كقفل ينفذ المزامنة. يمكن استخدام الكلمة الرئيسية المتزامنة لتعديل طرق الكائن والأساليب الثابتة وكتل التعليمات البرمجية. عند تعديل طرق الكائن والطرق الثابتة ، يكون القفل هو الكائن الذي توجد فيه الطريقة وكائن الفئة. عند تعديل كتلة الكود ، يجب توفير كائنات إضافية كأقفال. السبب في أن كل كائن Java يمكن استخدامه كقفل هو أن كائن شاشة (معالجة) يرتبط في رأس الكائن. عندما يدخل مؤشر الترابط إلى كتلة التعليمات البرمجية المتزامنة ، فإنه سيعقد تلقائيًا كائن الشاشة ، وعندما يخرج ، سيصدر كائن الشاشة تلقائيًا. عند الاحتفاظ بكائن الشاشة ، سيتم حظر مؤشرات الترابط الأخرى. بالطبع ، يتم تنفيذ عمليات التزامن هذه بواسطة طبقة JVM الأساسية ، ولكن لا تزال هناك بعض الاختلافات في التنفيذ الأساسي لطريقة تعديل الكلمات الرئيسية المتزامنة وكتلة الكود. تتم مزامنة طريقة تعديل الكلمات الرئيسية المتزامنة ضمنياً ، أي أنه لا يلزم التحكم فيه من خلال تعليمات رمز Bytecode. يمكن لـ JVM التمييز بين ما إذا كانت الطريقة هي طريقة متزامنة تعتمد على علامة الوصول ACC_Synchronized في جدول الطريقة ؛ بينما تتم مزامنة كتل التعليمات البرمجية المعدلة بواسطة الكلمة الرئيسية المتزامنة بشكل صريح ، والتي تتحكم في احتجاز مؤشر الترابط وإطلاق خط الأنابيب من خلال تعليمات الرموز المتجددة في المراقبة و monitorexit. يحمل كائن الشاشة حقل _count داخليًا. _count يساوي 0 يعني أن خط الأنابيب غير محتفظ ، و _count أكبر من 0 يعني أن خط الأنابيب قد تم الاحتفاظ به. في كل مرة يتم فيها إضافة مؤشر ترابط الحجز ، ستتم إضافة _count 1 ، وفي كل مرة يخرج مؤشر ترابط الحجز ، سيتم تقليل _count بمقدار 1. هذا هو مبدأ التنفيذ لإعادة دخول القفل المدمج. بالإضافة إلى ذلك ، هناك طابوران داخل كائن الشاشة _entryList و _waitset ، والتي تتوافق مع قائمة انتظار المزامنة وقائمة الانتظار الشرطي لـ AQS. عندما يفشل الخيط في الحصول على القفل ، سيتم حظره في _entryList. عندما يتم استدعاء طريقة الانتظار لكائن القفل ، سيقوم مؤشر الترابط بإدخال _waitset للانتظار. هذا هو مبدأ التنفيذ لمزامنة الخيط والانتظار الشرطي للأقفال المدمجة.
2. المقارنة بين reentrantlock ومزامنة
الكلمة الرئيسية المتزامنة هي آلية قفل مدمجة توفرها Java. يتم تنفيذ عمليات التزامن من قبل JVM الأساسي. REENTRANTLOCK عبارة عن قفل صريح يوفره حزمة java.util.concurrent ، ويتم تشغيل عمليات التزامن بواسطة مزامنة AQS. يوفر REENTRANTLOCK نفس الدلالات في القفل والذاكرة كأقفال مدمجة ، بالإضافة إلى ذلك ، يوفر بعض الميزات الأخرى بما في ذلك انتظار القفل المحدد ، وانتظار القفل القابل للمقاطعة ، والقفل العادل ، وتنفيذ قفل غير منظم. بالإضافة إلى ذلك ، كان لدى Reentrantlock أيضًا مزايا أداء معينة في إصدارات JDK المبكرة. نظرًا لأن Reentrantlock لديه العديد من المزايا ، فلماذا نستخدم الكلمة الرئيسية المتزامنة؟ في الواقع ، يستخدم الكثير من الناس إعادة إينترانتلوك لاستبدال تشغيل القفل للكلمات الرئيسية المتزامنة. ومع ذلك ، لا تزال الأقفال المدمجة لها مزاياها الخاصة. الأقفال المدمجة مألوفة للعديد من المطورين وأكثر بساطة وضغوط في الاستخدام. نظرًا لأنه يجب أن تسمى الأقفال الصريحة يدويًا في الكتلة الأخيرة ، فمن الأكثر أمانًا نسبيًا لاستخدام الأقفال المدمجة. في الوقت نفسه ، من المرجح أن يحسن أداء متزامن بدلاً من إعادة إدخاله في المستقبل. نظرًا لأن Synchronized عبارة عن خاصية مدمجة لـ JVM ، يمكن أن تؤدي بعض التحسينات ، مثل تحسين القفل على كائنات القفل المغلقة الخيط ، مما يلغي مزامنة الأقفال المدمجة عن طريق زيادة التفاصيل القوية ، وإذا تم تنفيذ هذه الوظائف من خلال أقفال قائمة على المكتبة ، فهذا أمر غير محتمل. لذلك عندما تكون هناك حاجة إلى بعض الميزات المتقدمة ، يجب استخدام إعادة الدخول ، والتي تشمل: عمليات اكتساب القفل القابلة للاتصال والقابلة للاتصالات والقابلة للاتصال ، وقوائم الانتظار العادلة ، وأقفال بنية غير متوفرة. خلاف ذلك ، يجب أن تستخدم المزامنة أولاً.
3. عمليات الحصول على الأقفال والإفراج عنها
دعونا نلقي نظرة أولاً على رمز العينة باستخدام reentrantlock لإضافة أقفال.
public void dosomething () {// الافتراضي هو الحصول على قفل reentrantlock قفل غير fair = جديد reentrantlock () ؛ حاول {// lock.lock () قبل التنفيذ ؛ // تنفيذ العملية ...} أخيرًا {// القفل. }}فيما يلي واجهة برمجة التطبيقات لاكتساب الأقفال والإفراج عنها.
// تشغيل الحصول على قفل قفل الفراغ العام () {sync.lock () ؛} // تشغيل إطلاق قفل الفراغ العام () {sync.Release (1) ؛}يمكنك أن ترى أن عمليات الحصول على القفل وإطلاق القفل يتم تفويضها إلى طريقة القفل وطريقة إطلاق كائن المزامنة على التوالي.
الطبقة العامة reentrantlock تنفس قفل ، java.io.serializable {sync المزامنة النهائية الخاصة ؛ ملخص فئة ثابتة يمتد ملخص agrussequeuedsynchronizer }.يحمل كل كائن reentrantlock مرجعًا لنوع المزامنة. فئة المزامنة هذه هي فئة داخلية مجردة. يرث من التجريدي extriedsynchronizer. طريقة القفل بداخلها هي طريقة مجردة. يتم تعيين قيمة متغير عضو REENTRANTLOCK أثناء البناء. دعونا نلقي نظرة على ما تفعله طريقتان مُنشئين في إعادة إيندرانتلوك؟
. New Fairsync (): New Nonfairsync () ؛}
سيقوم استدعاء مُنشئ المعلمة الافتراضي بتعيين مثيل Nonfairsync للمزامنة ، والقفل هو قفل غير فاصل في هذا الوقت. يتيح منشئ المعلمة المعلمات لتحديد ما إذا كان سيتم تعيين مثيل FairSync أو مثيل غير fairsync للمزامنة. يرث كل من Nonfairsync و Fairsync من فئة Sync وإعادة كتابة طريقة Lock () ، لذلك هناك بعض الاختلافات بين الأقفال العادلة والأقفال غير المقدمة في طريق الحصول على الأقفال ، والتي سنتحدث عنها أدناه. دعونا نلقي نظرة على تشغيل القفل. في كل مرة تقوم فيها بالاتصال بطريقة Unlock () ، يمكنك تنفيذ عملية Sync.Release (1). ستستدعي هذه العملية طريقة الإصدار () لفئة AbstractQueuedSynchronizer. دعنا نراجعها مرة أخرى.
// قم بإطلاق عملية القفل (الوضع الحصري) إصدار Boolean النهائي العام (int arg) {// قم بتحويل قفل كلمة المرور لمعرفة ما إذا كان يمكن إلغاء قفل إذا (tryRelease (arg)) {// احصل على عقدة الرأس H = Head ؛ // إذا لم تكن عقدة الرأس فارغة ولم تكن حالة الانتظار مساوية لـ 0 ، فقم باستيقاظ عقدة الخلف إذا (h! = null && } إعادة صواب ؛ } إرجاع خطأ ؛}طريقة الإصدار هذه هي واجهة برمجة التطبيقات لإطلاق عمليات القفل التي توفرها AQS. يقوم أولاً باستدعاء طريقة TryRelease لمحاولة الحصول على القفل. طريقة TryRelease هي طريقة مجردة ، ومنطق تنفيذها في مزامنة الفئة الفرعية.
// حاول إطلاق القفل النهائي المحمي Boolean TryRelease (int reease) {int c = getState () - الإصدارات ؛ // إذا لم يكن مؤشر الترابط الذي يحمل القفل هو مؤشر الترابط الحالي ، فسيتم طرح استثناء إذا (thread.currentThread ()! = getExClusiveOwnerThread ()) {رمي جديد intervalyitorstateException () ؛ } boolean free = false ؛ // إذا كانت حالة المزامنة 0 ، فهذا يعني أنه يتم إصدار القفل إذا (C == 0) {// قم بتعيين علامة القفل الذي يتم إصداره على أنه حقيقي = صحيح ؛ . } setState (c) ؛ العودة مجانًا ؛}ستحصل طريقة TryRelease أولاً على حالة المزامنة الحالية ، وطرح حالة التزامن الحالية من المعلمات التي تم تمريرها إلى حالة التزامن الجديدة ، ثم تحديد ما إذا كانت حالة التزامن الجديدة تساوي 0. إذا كانت تساوي 0 ، فهذا يعني أنه يتم إصدار القفل الحالي. ثم قم بتعيين حالة إصدار القفل على TRUE ، ثم قم بمسح الخيط الذي يحتل القفل حاليًا ، وأخيراً استدعاء طريقة setState لتعيين حالة التزامن الجديدة وإعادة حالة إطلاق القفل.
4. قفل عادل وقفل غير عادل
نحن نعرف أي مثال محدد هو إعادة إينترانتلوك التي تشير إليها بناءً على المزامنة. أثناء البناء ، سيتم تعيين مزامنة متغير العضو. إذا تم تعيين القيمة إلى مثيل Nonfairsync ، فهذا يعني أنه قفل غير مقصود ، وإذا تم تعيين القيمة إلى مثيل Fairsync ، فهذا يعني أنه قفل عادل. إذا كان ذلك قفلًا عادلًا ، فسيحصل مؤشرات الترابط على القفل بالترتيب الذي يقدمون به الطلبات ، ولكن في القفل غير العادل ، يُسمح بسلوك القطع: عندما يطلب مؤشر ترابط قفلًا غير عادل ، إذا أصبحت حالة القفل متوفرة في نفس الوقت الذي يتم فيه إصدار الطلب ، فإن مؤشر الترابط سيتخطى جميع مؤشرات الترابط في قائمة الانتظار للحصول على القفل مباشرة. دعونا نلقي نظرة على كيفية الحصول على أقفال غير عادلة.
// يمتد الفئة النهائية المتزامنة غير العادلة Nonfairsync Sync {// تنفيذ الطريقة التجريدية للفئة الأصل لاكتساب قفل الفراغ النهائي القفل () {// استخدم طريقة CAS لتعيين حالة المزامنة إذا (contereAndSetTate (0 ، 1)) {// إذا نجح الإعداد ، فهذا يعني أن القفل ليس محتلًا (thread. } آخر {// وإلا فهذا يعني أن القفل قد تم احتلاله ، والمكالمات اكتساب ودع موضوع قائمة الانتظار لمزامنة قائمة الانتظار للحصول على الحصول (1) ؛ }} // طريقة محاولة الحصول على القفل النهائي المحمي Boolean Tryacquire (int quatiros) {return nonfairtryacquire (Accouns) ؛ }} // الحصول على الأقفال في الوضع غير المترابط (الوضع الحصري) الحصول على الفراغ النهائي العام (int arg) {if (! tryacquire (arg) && quiuirequeued (addwaiter (node.exclusive) ، arg)) {selfinterrupt () ؛ }}يمكن ملاحظة أنه في طريقة قفل القفل غير العادلة ، سيغير الخيط قيمة حالة التزامن من 0 إلى 1 في الخطوة الأولى في CAS. في الواقع ، هذه العملية تعادل محاولة الحصول على القفل. إذا كان التغيير ناجحًا ، فهذا يعني أن الخيط قد اكتسب القفل الآن ، ولم يعد هناك حاجة إلى طابور في قائمة انتظار المزامنة بعد الآن. إذا فشل التغيير ، فهذا يعني أنه لم يتم إصدار القفل عندما يأتي مؤشر الترابط لأول مرة ، لذلك تسمى طريقة الحصول على التالي. نحن نعلم أن هذه الطريقة المكتسبة ورثت من طريقة الملخص. دعنا نراجع هذه الطريقة. بعد أن يدخل مؤشر الترابط إلى طريقة اكتساب ، اتصل بأسلوب TryAcquire الأول لمحاولة الحصول على القفل. نظرًا لأن Nonfairsync يكتب طريقة TryAcquire ويطلق على طريقة غير المجالات لمزامنة الفئة الأصل في هذه الطريقة ، فسيتم استدعاء طريقة غير المجالات هنا لمحاولة الحصول على القفل. دعونا نرى ما تفعله هذه الطريقة على وجه التحديد.
// الاستحواذ غير العادل للقفل النهائي المنطقي nonfairtryaCquire (int quistires) {// الحصول على مؤشر الترابط النهائي الحالي الحالي = thread.currentThRead () ؛ // الحصول على حالة التزامن الحالية int c = getState () ؛ // إذا كانت حالة التزامن 0 ، فهذا يعني أن القفل غير مشغل إذا (C == 0) {// استخدم CAS لتحديث حالة التزامن إذا (ConperteAndSetState (0 ، الحصول)) {// قم بتعيين مؤشر الترابط حاليًا يشغل SetexClusOwTherThread (الحالي) ؛ العودة صحيح. } // خلاف ذلك ، يتم تحديد ما إذا كان القفل هو مؤشر الترابط الحالي} آخر إذا (الحالي == getExClusiveOwnerThread ()) {// إذا كان القفل محتجزًا بواسطة مؤشر الترابط الحالي ، فقم بتعديل حالة التزامن الحالية بشكل مباشر intc = c + if (nextC <0) {رمي خطأ جديد ("تم تجاوز عدد القفل القصوى") ؛ } setState (nextC) ؛ العودة صحيح. } // إذا لم يكن القفل هو مؤشر الترابط الحالي ، فأرجع إلى عودة العلامة الخاطئة ؛}طريقة غير المجالات هي طريقة المزامنة. يمكننا أن نرى أنه بعد دخول الخيط هذه الطريقة ، فإنه يكتسب أولاً حالة التزامن. إذا كانت حالة التزامن 0 ، فاستخدم عملية CAS لتغيير حالة التزامن. في الواقع ، هذا هو الحصول على القفل مرة أخرى. إذا لم تكن حالة التزامن 0 ، فهذا يعني أن القفل مشغول. في هذا الوقت ، سنحدد أولاً ما إذا كان الخيط الذي يحمل القفل هو الخيط الحالي. إذا كان الأمر كذلك ، فسيتم زيادة حالة التزامن بحلول 1. وإلا ، فإن عملية محاولة الحصول على القفل ستفشل. لذلك سيتم استدعاء طريقة AddWaiter لإضافة مؤشر الترابط إلى قائمة انتظار المزامنة. لتلخيص ، في وضع القفل غير العادل ، سيحاول مؤشر الترابط الحصول على قفلتين قبل إدخال قائمة انتظار المزامنة. إذا نجحت عملية الاستحواذ ، فلن تدخل قائمة انتظار قائمة انتظار قائمة انتظار قائمة انتظار المزامنة ، وإلا فإنها ستدخل قائمة انتظار قائمة انتظار قائمة انتظار قائمة انتظار المزامنة. بعد ذلك ، دعونا نلقي نظرة على كيفية الحصول على أقفال عادلة.
// Synchronizer التي تنفذ قفلًا فادحًا فئة نهائية FARSYNC يمتد SYNC {// تنفيذ الطريقة التجريدية للفئة الأصل للحصول على قفل قفل الفراغ النهائي () {// Call Access ودع سلسلة رسائل الانتظار لمزامنة قائمة الانتظار للحصول على الحصول على (1) ؛ } // حاول الحصول على قفل Boolean Final TryAcquire (int quics) {// الحصول على مؤشر الترابط النهائي الحالي الحالي = thread.currentThread () ؛ // الحصول على حالة التزامن الحالية int c = getState () ؛ // إذا كانت حالة التزامن 0 تعني أن القفل لا يشغل إذا (c == 0) {// الدفاع عما إذا كانت هناك عقدة أمامية في قائمة انتظار المزامنة إذا (! hasqueuedpredecursors () setExClusiveOwnerThread (الحالي) ؛ العودة صحيح. } // خلاف ذلك ، حدد ما إذا كان مؤشر الترابط الحالي يحتفظ بالقفل} آخر إذا (الحالي == getExClusiveOwnerThread ()) {// إذا كان مؤشر الترابط الحالي يحتفظ بالقفل ، فقم بتعديل حالة التزامن بشكل مباشر intc = c + if (nextC <0) {رمي خطأ جديد ("تم تجاوز عدد القفل القصوى") ؛ } setState (nextC) ؛ العودة صحيح. } // إذا كان الخيط الحالي لا يحتفظ بالقفل ، فإن عملية الاستحواذ تفشل في العودة خاطئة ؛ }} عند استدعاء طريقة قفل القفل العادل ، سيتم استدعاء طريقة اكتساب مباشرة. وبالمثل ، تستدعي طريقة الاستحواذ أولاً طريقة TryAcquire Fairsync لمحاولة الحصول على القفل. في هذه الطريقة ، يتم الحصول على قيمة حالة التزامن لأول مرة. إذا كانت حالة التزامن 0 ، فهذا يعني أن القفل يتم إصداره في هذا الوقت. الفرق من القفل غير العادل هو أنه سيتصل أولاً بطريقة HasqueuedPredecursors للتحقق مما إذا كان شخص ما يصطف في قائمة انتظار المزامنة. إذا لم يكن أحد يصطف في الانتظار ، فسيتم تعديل قيمة حالة التزامن. يمكنك أن ترى أن القفل العادل يتبنى طريقة مجاملة هنا بدلاً من الحصول على القفل على الفور. باستثناء هذه الخطوة التي تختلف عن القفل غير العادل ، فإن العمليات الأخرى هي نفسها. لتلخيص ، يمكننا أن نرى أن القفل العادل لا يتحقق إلا من حالة القفل مرة واحدة قبل إدخال قائمة انتظار المزامنة. حتى لو وجدت أن القفل مفتوح ، فلن تحصل عليه على الفور. بدلاً من ذلك ، سوف تدع المواضيع في قائمة انتظار المزامنة تكتسبها أولاً. لذلك ، يمكن التأكد من أن الترتيب الذي تحصل عليه جميع المواضيع تحصل على الأقفال تحت القفل العادل هو أولاً ثم وصوله ، مما يضمن أيضًا عدالة الحصول على الأقفال.
فلماذا لا نريد أن تكون كل الأقفال عادلة؟ بعد كل شيء ، الإنصاف هو سلوك جيد ، والظلم هو سلوك سيء. نظرًا لأن عمليات التعليق والإغلاق الخاصة بالخيط لها النفقات العامة الكبيرة ، فإنها تؤثر على أداء النظام ، خاصة في حالة المنافسة الشرسة ، ستؤدي الأقفال العادلة إلى عمليات تعليق وتستيقظ متكررة للخيوط ، في حين أن الأقفال غير المقصودة يمكن أن تقلل من هذه العمليات ، لذلك ستكون أفضل من أقفال الأداء في الأداء. بالإضافة إلى ذلك ، نظرًا لأن معظم المواضيع تستخدم الأقفال لفترة قصيرة جدًا ، وستكون عملية الاستيقاظ الخاصة بالخيط تأخيرًا ، فمن الممكن أن يحصل مؤشر الترابط B على القفل على الفور ويطلق القفل بعد استخدامه. هذا يؤدي إلى وضع الفوز. في اللحظة التي يتم فيها الحصول على مؤشر الترابط A ، لا يتأخر القفل ، ولكن يستخدم الخيط B القفل مقدمًا وتم تحسين إنتاجه أيضًا.
5. آلية تنفيذ قوائم الانتظار الشرطية
هناك بعض العيوب في قائمة انتظار الحالة المدمجة. يمكن أن يكون لكل قفل مدمج فقط قائمة انتظار حالة واحدة مرتبطة ، والتي تتسبب في انتظار مؤشرات ترابط متعددة لتنبؤ حالة مختلفة في قائمة انتظار الحالة نفسها. ثم ، في كل مرة يتم فيها تسمى الإخطار ، سيتم إيقاظ جميع خيوط الانتظار. عندما يستيقظ الخيط ، يجد أنه ليس الشرط الذي ينتظره ، وسيتم تعليقه. وهذا يؤدي إلى العديد من عمليات الاستيقاظ والتعليق غير المجدية ، والتي ستضيع الكثير من موارد النظام وتقليل أداء النظام. إذا كنت ترغب في كتابة كائن متزامن له مسببات مشروطة متعددة ، أو إذا كنت ترغب في الحصول على عنصر تحكم أكثر من رؤية قائمة الانتظار الشرطية ، فأنت بحاجة إلى استخدام قفل وحالة صريحة بدلاً من الأقفال المدمجة وقوائم الانتظار الشرطية. ترتبط حالة وقفل معًا ، تمامًا مثل قائمة انتظار الحالة وقفل مدمج. لإنشاء شرط ، يمكنك استدعاء طريقة القفل. دعونا نلقي نظرة أولاً على مثال باستخدام الحالة.
الفئة العامة probedbuffer {final lock lock = new reentrantlock () ؛ الحالة النهائية notfull = lock.newcondition () ؛ // شرط المسند: الشرط النهائي notfull notempty = lock.newcondition () ؛ // الشرط المسد: الكائن النهائي غير المألوف [] عناصر = كائن جديد [100] ؛ int putptr ، takeptr ، count ؛ // طريقة الإنتاج Public Void pub (Object x) remrows interruptedException {lock.lock () ؛ حاول {بينما (count == items.length) notfl.await () ؛ // قائمة الانتظار ممتلئة ، وينتظر الخيط العناصر [putptr] في قائمة الانتظار الشديدة. العناصر [putptr] = x ؛ if (++ putptr == items.length) putptr = 0 ؛ ++ العد ؛ notempty.signal () ؛ // الإنتاج ناجح ، استيقظ على عقدة قائمة انتظار غير المألوفة} أخيرًا {lock.unlock () ؛ }} // طريقة الاستهلاك الكائن العام take () رميات interruptedException {lock.lock () ؛ حاول {بينما (count == 0) notempty.await () ؛ // قائمة الانتظار فارغة ، ينتظر مؤشر ترابط الكائن x = العناصر [takeptr] في قائمة انتظار غير المألوفة ؛ if (++ takeptr == items.length) takeptr = 0 ؛ --عدد؛ notfull.signal () ؛ // الاستهلاك ناجح ، استيقظ على عقدة قائمة الانتظار الشديدة X ؛ } أخيرًا {lock.unlock () ؛ }}}يمكن أن يولد كائن قفل قوائم انتظار متعددة ، ويتم إنشاء قوائم انتظار شرطين هنا غير شديدة الإلغاء. عندما تكون الحاوية ممتلئة ، يجب حظر الخيط الذي يستدعي طريقة PUT. انتظر حتى يصبح المسند صحيح (الحاوية غير راضية) يستيقظ ويستمر في التنفيذ ؛ عندما تكون الحاوية فارغة ، يجب حظر الخيط الذي يستدعي طريقة أخذ. انتظر حتى يصبح مسند الحالة صحيحًا (الحاوية غير فارغة) يستيقظ ويستمر في التنفيذ. ينتظر هذان النوعان من المواضيع وفقًا لتنبؤات حالة مختلفة ، لذلك سوف يدخلان في طوابير شرطين مختلفين لحظره ، والانتظار حتى الوقت المناسب قبل الاستيقاظ عن طريق استدعاء واجهة برمجة التطبيقات على كائن الحالة. فيما يلي رمز التنفيذ لطريقة الشرط الجديد.
// إنشاء حالة قائمة انتظار شرط عام newCondition () {return sync.newcondition () ؛} يمتد Sync static static المجردة التجريدية {// إنشاء كائن حالة جديد inctionObject NewCondition () {return new inctionObject () ؛ }}يعتمد تنفيذ قائمة انتظار الشرط على reentrantlock على ustructiqueuedsynchronizer. كائن الشرط الذي نحصل عليه عند استدعاء طريقة الشرط الجديد هو مثيل لشرط الفئة الداخلية لـ AQS. تتم جميع عمليات قوائم قوائم الحالة عن طريق استدعاء واجهة برمجة التطبيقات التي توفرها MonderObject. للتنفيذ المحدد لـ InctionObject ، يمكنك التحقق من مقالتي "سلسلة Concurrency Java [4] ----- AccterqueuedSeuedSynchronizer Source Code تحليل قائمة انتظار شرطية" ولن أكررها هنا. في هذه المرحلة ، انتهى تحليلنا لرمز المصدر لـ REENTRANTLOCK. آمل أن تساعد قراءة هذه المقالة القراء على فهم وإعادة إدراج.
ما سبق هو كل محتوى هذه المقالة. آمل أن يكون ذلك مفيدًا لتعلم الجميع وآمل أن يدعم الجميع wulin.com أكثر.