CountDownlatch هي فئة أدوات مفيدة. باستخدامه ، يمكننا اعتراض واحد أو أكثر من مؤشرات الترابط لتنفيذها بعد حالة معينة. يوفر محتوىه الداخلي عدادًا ، ويجب تحديد القيمة الأولية للعداد عند إنشاء قفل ، ويجب أن تكون القيمة الأولية للعداد أكبر من 0. بالإضافة إلى ذلك ، فإنها توفر أيضًا طريقة العد التنازلي لتشغيل القيمة المضادة. سيتم تقليل العداد بمقدار 1 في كل مرة تسمى طريقة العد التنازلي. عندما يتم تقليل قيمة العداد إلى 0 ، فهذا يعني أن الشروط ناضجة ، وسيتم إيقاظ جميع الخيوط عن طريق استدعاء طريقة الانتظار. هذه هي الآلية الداخلية لـ CountDownlatch. يبدو الأمر بسيطًا للغاية ، فهو ليس أكثر من حظر بعض الخيوط والسماح لهم بالتنفيذ بعد الوصول إلى حالة معينة. ومع ذلك ، فإن CountDownlatch لديه مجموعة واسعة من سيناريوهات التطبيق. طالما أن لديك عقلًا كبيرًا واستخدامه ، يمكنك لعب الحيل المختلفة. سيناريو التطبيق الأكثر شيوعًا هو تمكين مؤشرات ترابط متعددة من تنفيذ مهمة في نفس الوقت ، ثم حساب النتائج وتلخيصها بعد تنفيذ جميع المهام. يوضح الشكل التالي ديناميكي عملية حظر الخيوط الكاملة.
يوضح الشكل أعلاه أنه يتم حظر 5 مؤشرات ترابط عن طريق استدعاء طريقة الانتظار ، ويجب أن تنتظر انخفاض قيمة العداد إلى 0 قبل الاستمرار في التنفيذ. يتم تحديد القيمة الأولية للعداد عند إنشاء قفل ، ويتم تقليلها لاحقًا بمقدار 1 مع كل مكالمة إلى طريقة العد التنازلي. ينشر الكود التالي طريقة إنشاء CountDownlatch.
// Constructor Public CountDownLatch (int count) {if (count <0) refl new alficalargumentException ("count <0") ؛ this.sync = new sync (count) ؛ }يحتوي CountDownlatch على مُنشئ معلمة واحد فقط ، ويجب تمرير قيمة أكبر من 0 كقيمة أولية للعداد ، وإلا سيتم الإبلاغ عن خطأ. يمكنك أن ترى أنه في المُنشئ ، فقط كائن مزامن جديد وتعيينه لمزامنة متغير العضو. مثل فئات أدوات التزامن الأخرى ، يعتمد تنفيذ CountDownLatch على AQS ، وهو تطبيق في الوضع المشترك AQS. يقوم CountDownlatch بتنفيذ مزامنة فئة داخلية ويستخدمها لروث AQS ، بحيث يمكن استخدام معظم الطرق التي توفرها AQS. دعنا نلقي نظرة على كود الفئة الداخلية المزامنة.
// Synchronizer Private Static Class Sync ExtructiveQueuedSynchronizer {// constructor sync (int count) {setState (count) ؛ } // الحصول على حالة التزامن الحالية int getCount () {return getState () ؛ } // حاول الحصول على الرقم السلبي لقفل // الإرجاع: يشير إلى أن مؤشر الترابط الحالي فشل في الحصول على // إرجاع القيمة صفر: يشير إلى أنه تم الحصول على مؤشر الترابط الحالي بنجاح ، ولكن لم يعد بإمكان مؤشر الترابط اللاحق الحصول على // إرجاع الرقم الإيجابي: يشير إلى أن الخيط الحالي قد تم الحصول عليه بنجاح ، ويمكن أيضًا الحصول على سلاسل لاحقة الحصول على نجاح محمي int int int) (int {getStines () 1: -1 ؛ } // حاول إطلاق القفل المحمي Boolean Tryreleaseshared (int reease) {for (؛؛) {// الحصول على حالة التزامن int c = getState () ؛ // إذا كانت حالة التزامن 0 ، إذا (c == 0) {return false ؛ } // خلاف ذلك ، قلل من حالة التزامن بمقدار 1 int nextc = c-1 ؛ // استخدم طريقة CAS لتحديث حالة التزامن if (CompareAndSetState (c ، nextc)) {return nextc == 0 ؛ }}}}يمكنك أن ترى أن مُنشئ Sync سيقوم بتعيين قيمة حالة التزامن على قيمة المعلمة التي تم تمريرها. بعد ذلك ، في كل مرة تسمى طريقة العد التنازلي ، سيتم تقليل قيمة الحالة المتزامنة بمقدار 1 ، وهو مبدأ التنفيذ للعداد. الأساليب الأكثر استخدامًا بشكل شيوع عند استخدام فئة أدوات CountDownLatch هما طريقة الانتظار وطريقة العد التنازلي. سيؤدي استدعاء طريقة الانتظار إلى حظر الخيط الحالي حتى يكون العداد 0 ، وسيقوم استدعاء طريقة العد التنازلي بتقليل قيمة العداد بمقدار 1 حتى يتم تقليله إلى 0. دعنا نلقي نظرة على كيفية استدعاء طريقة الانتظار.
// تسبب في انتظار مؤشر الترابط الحالي حتى يتناقص المزلاج إلى 0 ، أو يتم مقاطعة الخيط الفراغ العام في Await () رميات interruptedException {// الحصول (thread.Interreded ()) {رمي جديد interruptedException () ؛ } // 1. حاول الحصول على القفل if (tryacquireshared (arg) <0) {// 2. إذا فشل عملية الاستحواذ ، أدخل الطريقة doacquiresharedInterrupturedruptireruptireruptreructible (arg) ؛ }}عندما يستدعي الخيط طريقة الانتظار ، فإنه يتصل فعليًا بالطريقة التي يتم الحصول عليها من AQS. هذه الطريقة تكتسب القفل استجابة لانقطاع الخيط. يتم نشر رمز هذه الطريقة أيضًا أعلاه. يمكننا أن نرى أنه في الطريقة التي يتم الحصول عليها ، أولاً ، سوف يطلق على طريقة TryAcquireshared لمحاولة الحصول على القفل. نرى منطق طريقة TryAcquireshared إعادة كتابة في المزامنة. منطق تنفيذ الطريقة بسيط للغاية ، وهو الحكم على ما إذا كانت حالة المزامنة الحالية هي 0. إذا كانت 0 ، فإن العائد 1 يعني أنه يمكن الحصول على القفل ، وإلا فإن الإرجاع -1 يعني أنه لا يمكن الحصول على القفل. إذا تم إرجاع طريقة TryAcquireshared 1 ، فيمكن أن يستمر مؤشر الترابط في التنفيذ دون انتظار. إذا تم إرجاع -1 ، فسيتم استدعاء طريقة doacquiresharedInterruptluredruptluredruptible في قائمة الانتظار المتزامنة للانتظار. هذا هو مبدأ أن استدعاء طريقة الانتظار سيمنع الخيط الحالي. دعونا نرى كيف تستيقظ طريقة العد التنازلي لخيط الحظر.
// طريقة لتقليل العد التنازلي الفراغ العام المزلاج () {sync.releaseshared (1) ؛} // عملية الإصدار (الوضع المشترك) النهائي النهائي النهائي (int arg) {// 1. حاول إطلاق القفل إذا (tryreleaseshared (arg)) {// 2. إذا كان الإصدار ناجحًا ، استيقظ مؤشرات ترابط أخرى doreleaseshared () ؛ العودة صحيح. } إرجاع خطأ ؛}يمكنك أن ترى أن طريقة RELEASESSERADE تسمى في طريقة العد التنازلي. هذه الطريقة هي أيضًا طريقة في AQS. لقد نشرنا أيضًا رمزها عليها. أول شيء في طريقة RebeAseshared هو استدعاء طريقة Tryreleaseshared لمحاولة إطلاق القفل. طريقة Tryreleaseshared هي طريقة مجردة في AQS. منطق التنفيذ المحدد هو في فئة مزامنة الفئة الفرعية. يمكننا العثور على هذه الطريقة في رمز فئة المزامنة المنشورة أعلاه. إذا كانت طريقة TryReleaseshared تُرجع إلى الإصدار ، وإرجاع خطأ لإطلاق الفشل. لن يعود إلا إذا كانت حالة التزامن هي بالضبط 0 بعد انخفاض 1. في حالات أخرى ، سيتم إرجاع خطأ. ثم عندما يعود Tryreleaseshare بشكل صحيح ، سيتم استدعاء طريقة DoreleAseshared على الفور لإيقاظ جميع الخيوط في قائمة انتظار المزامنة. وهذا ما يفسر سبب آخر مرة يتم فيها استدعاء طريقة العد التنازلي لتقليل العداد إلى 0 ستستيقظ جميع المواضيع المحظورة. هذه هي المبادئ الأساسية لـ CountDownlatch. دعونا نلقي نظرة على مثال على استخدامه.
سيناريو التطبيق: عند لعب Happy Mountlord ، يجب أن تنتظر وصول جميع اللاعبين الثلاثة قبل أن تتمكن من التعامل مع البطاقات.
لاعب الفئة العامة يمتد الموضوع {private static int count = 1 ؛ معرف int النهائي الخاص = count ++ ؛ قفل العد التنازلي الخاص ؛ اللاعب العام (CountDownLatch Latch) {this.latch = latch ؛ } Override public void run () {system.out.println ("【player" + id + "] entry") ؛ latch.countdown () ؛ } رميات الفراغ الثابتة العامة (سلسلة [] args) interruptedException {countDownLatch latch = new CountDownLatch (3) ؛ System.out.println ("تبدأ اللعبة ، في انتظار دخول اللاعب ...") ؛ لاعب جديد (مزلاج) .start () ؛ لاعب جديد (مزلاج) .start () ؛ لاعب جديد (مزلاج) .start () ؛ latch.await () ؛ System.out.println ("وصل اللاعبون ، ابدأوا في التعامل ...") ؛ }}تظهر نتائج العملية أنه يجب تنفيذ عملية التعامل بعد دخول جميع اللاعبين إلى الحقل. نعلق على 23 سطر Latch.await () ونقارنه لرؤية النتائج.
يمكنك أن ترى أنه بعد التعليق على Late Latch.await () ، لا يضمن أن جميع اللاعبين سيبدأون في التعامل مع البطاقات فقط بعد دخول الحقل.
ما سبق هو كل محتوى هذه المقالة. آمل أن يكون ذلك مفيدًا لتعلم الجميع وآمل أن يدعم الجميع wulin.com أكثر.