تحليل رمز المصدر العددي - AWAIT () ، المحتوى المحدد هو كما يلي
تحدثت المقالة السابقة عن كيفية استخدام CountDownlatch. سيتحدث هذه المقالة عن مبدأ AWAIT () من مستوى رمز المصدر.
نحن نعلم بالفعل أنه يمكن أن تبقي الخيط الحالي في حالة حظر حتى يصبح عدد المزلاج صفرًا (أو مقاطعة خيط).
فيما يلي رمز المصدر.
end.await () ؛ ↓ void public await () رميات interruptedException {sync.acquiresharedInterruptive (1) ؛}Sync هي الفئة الداخلية من CountDownlatch. هنا هو تعريفها.
يمتد Sync من الفئة النهائية الثابتة الخاصة إلى ExclostKuedsynchronizer {...}يرث مجردة succedseuedsynchronizer. AbstractQueuedSynchronizer ينتمي هذه الفئة إلى فئة مهمة للغاية في خيوط Java.
يوفر إطار عمل لتنفيذ أقفال الحظر والمزامنة ذات الصلة (مثل الإشارات والأحداث وما إلى ذلك) التي تعتمد على قوائم انتظار FIFO.
استمر في الذهاب والقفز إلى فئة التجريدية.
sync.acquiresharedInterruptipluredructluredruptreruction (1) ؛ public public final void issuciresharedInterruptireruptireruptireruptireruptireruptireruptireruptireruptireruptireruptireruptireruptirerupt (int arg). إذا (tryacquireshared (arg) <0) doacquiresharedInterrupturedructible (arg) ؛}
هناك حكمان هنا. أولاً ، حدد ما إذا كان الخيط قد توقف ، ثم إصدار الحكم التالي. هنا ننظر أساسًا إلى الحكم الثاني.
محمية int tryacquireshared (int quatiros) {return (getState () == 0)؟ 1: -1 ؛}تجدر الإشارة إلى أن طريقة TryAcquireshared يتم تنفيذها في المزامنة.
على الرغم من وجود تطبيقات لها في مجردة exectuedsynchronizer ، فإن التنفيذ الافتراضي هو إلقاء استثناء.
TryAcquireshared يتم استخدام هذه الطريقة للاستعلام عما إذا كان يمكن السماح لحالة الكائن الحالي بالحصول على القفل.
يمكننا أن نرى أنه في المزامنة ، نعيد قيمة int المقابلة عن طريق تحديد ما إذا كانت الحالة 0.
إذن ماذا تعني الدولة؟
/*** حالة التزامن. */ الحالة المتطايرة الخاصة ؛
يوضح الكود أعلاه بوضوح أن الحالة تمثل حالة التزامن.
تجدر الإشارة إلى أن الحالة تستخدم الكلمة الرئيسية المتطايرة لتعديلها.
يمكن للكلمة الرئيسية المتطايرة التأكد من تحديث تعديل الحالة إلى الذاكرة الرئيسية على الفور. عندما تحتاج مؤشرات الترابط الأخرى إلى القراءة ، سيتم قراءة القيمة الجديدة في الذاكرة.
أي أن وضوح الدولة مضمونة. إنها أحدث البيانات.
ما هي الدولة التي تأتي هنا؟
هنا نحتاج إلى إلقاء نظرة على مُنشئ CountDownlatch.
CountDownLatch End = New CountDownLatch (2) ؛ ↓ CountDownLatch (int count) {if (count <0) رمي جديد alfictalargumentException ("count <0") ؛ this.sync = new sync (count) ؛} ↓ sync (int count) {setState (count) ؛}اتضح أن الأرقام في المنشئ تستخدم لضبط الحالة.
لذلك لدينا حالة == 2 هنا. Tryacquireshared يعود -1. أدخل أدناه
doacquiresharedInterruptiply (arg) ؛ private void doacquiresharedInterruptly (int arg) رمي interruptedException {Final Node Node = AddWaiter (node.shared) ؛ فشل Boolean = صحيح ؛ حاول {for (؛؛) {final node p = node.predecessor () ؛ if (p == head) {int r = tryacquireshared (arg) ؛ if (r> = 0) {setheAdandPropagate (node ، r) ؛ p.next = null ؛ // فشل مساعدة GC = false ؛ يعود؛ }} if (shouldparkafterFailedAcquire (p ، node) && parkandcheckinterrupt ()) رمي interruptededexception () ؛ }} أخيرًا {if (فشل) cancelAcquire (node) ؛ }}حسنًا ، هذا الرمز طويل بعض الشيء ، ويتم استدعاء العديد من الوظائف فيه. دعونا ننظر إليها واحدة تلو الأخرى.
تظهر عقدة فئة جديدة في السطر الأول.
العقدة هي فئة داخلية في AQS (AbstractQueuedSynchronizer) ، والتي تحدد بنية السلسلة. كما هو موضح أدناه.
+------+Prev+-----++-----+HEAD | | <---- | | <---- | | | ذيل +----- + +----- + +----- +
تذكر هذا الهيكل.
هناك أيضًا طريقة في السطر الأول من الكود AddWaiter (Node.shared).
AddWaiter (node.shared) //node.shared يعني أن العقدة في الوضع المشترك ↓ العقدة الخاصة AddWaiter (وضع العقدة) {node node = new node (thread.currentThread () ، mode) ؛ // جرب المسار السريع لـ ENQ ؛ النسخ الاحتياطي إلى ENQ الكامل على عقدة الفشل pred = tail ؛ // ذيل العقدة المؤقتة العابرة الخاصة ؛ if (pred! = null) {node.prev = pred ؛ if (compareAndSetTail (pred ، node)) {pred.next = node ؛ عقدة العودة. }} enq (node) ؛ عودة العقدة ؛}أولاً ، يتم إنشاء عقدة ويتم تخزين الخيط الحالي. الوضع هو وضع مشترك.
ذيل يعني أن نهاية قائمة الانتظار لقائمة انتظار الانتظار خالية في هذه اللحظة. لذلك pred == null يدخل ENQ (العقدة) ؛
enq (node) ↓ العقدة الخاصة enq (عقدة العقدة النهائية) {for (؛؛) {node t = tail ؛ إذا كان (t == null) {// يجب تهيئة IF (compareAndSetHead (node node ())) tail = head ؛ } آخر {node.prev = t ؛ if (compareAndSetTail (t ، node)) {t.next = node ؛ العودة ر ؛ }}}}نفس الذيل فارغ ، أدخل المقارنات.
CompareAndSetHead (New Node ()) ↓/*** CAS HEAD Field. تستخدم فقط من قبل ENQ. */Private Final Boolean CompareAndSethead (تحديث العقدة) {return unfafe.compareanswapobject (هذا ، Headoffset ، NULL ، UPDATE) ؛}هذه عملية CAS. إذا كان الرأس فارغًا ، فسيتم تعيين رأس قائمة انتظار الانتظار على قيمة التحديث ، وهي عقدة جديدة.
الذيل = الرأس ؛ ثم لم يعد الذيل فارغًا في هذا الوقت. أدخل الدورة التالية.
هذه المرة ، النقطة الأولى للمؤشر السابق للعقدة إلى الذيل ، ثم اضبط العقدة على الذيل من خلال عملية CAS ، وإعادة ذيل قائمة الانتظار ، أي العقدة.
يتغير نموذج قائمة انتظار الانتظار على النحو التالي
+ ------+ السابق +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
حسنًا ، عندما تصل إلى هنا ، تُرجع طريقة الانتظار ، فهي عبارة عن مؤشر ترابط يساوي عقدة الخيط الحالي.
ارجع إلى doacquiresharedInterruptly (int arg) وأدخل الحلقة التالية.
لـ (؛؛) {Final Node p = node.predecessor () ؛ if (p == head) {int r = tryacquireshared (arg) ؛ if (r> = 0) {setheAdandPropagate (node ، r) ؛ p.next = null ؛ // فشل مساعدة GC = false ؛ يعود؛ }} if (shouldparkafterfailedacquire (p ، node) && parkandcheckinterrupt ()) رمي interruptededexception () ؛}في هذا الوقت ، على افتراض أن الدولة لا تزال أكبر من 0 ، ثم r <0 في هذا الوقت ، لذلك أدخل طريقة يجب أن يكون parkafterfailedacquire.
يجب أن parkafterfailedacquire (p ، العقدة) ↓ يجب أن يكون boolean الثابت الخاص يجب أن يكون parkafterfailedacquire (عقدة pred ، عقدة العقدة) {int ws = pred.waitstatus ؛ if (ws == node.signal) // static final int signal = -1 ؛ / * * قامت هذه العقدة بالفعل بتعيين الحالة التي تطلب إصدارًا * للإشارة إليه ، بحيث يمكنها الوقوف بأمان. */ إرجاع صحيح ؛ إذا تم إلغاء (WS> 0) { / * * *. تخطي على الأسلاف و * المشار إليه. */ do {node.prev = pred = pred.prev ؛ } بينما (pred.waitstatus> 0) ؛ pred.next = العقدة ؛ } آخر { / * * يجب أن يكون WaitStatus 0 أو نشره. تشير إلى أننا * بحاجة إلى إشارة ، لكن لا تقف بعد. سيحتاج المتصل إلى * إعادة المحاولة للتأكد من أنه لا يمكن اكتسابه قبل وقوف السيارات. */ conteraNdSetWaitStatus (pred ، ws ، node.signal) ؛ } إرجاع FALSE ؛} ↓/*** CAS WAITSTATUS حقل العقدة. */private Static Final Boolean CompareAndSetWaitStatus (عقدة العقدة ، int توقع ، int update) {return unsafe.compareanswapint (Node ، WaitStatusoff ، توقع ، تحديث) ؛}يمكنك أن ترى أنه يجب أن يسير على ما يجب أن يتقارنه.
CompareAndSetWaitStatus اضبط WaitStatus من DERE إلى node.signal.
Node.Signal يعني أن الخيوط في العقد اللاحقة تحتاج إلى أن تكون غير معطلة (على غرار الاستيقاظ). هذه الطريقة تُرجع كاذبة.
بعد هذه الدورة ، يصبح نموذج قائمة الانتظار هو الحالة التالية
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
لأن يجب أن يعود يجب على parkafterfailedacquire كاذبة ، فلن ننظر بعد الآن إلى الشروط التالية. تابع الحلقة في (؛ ؛) ؛).
إذا كانت الدولة لا تزال أكبر من 0 ، فأدخل مرة أخرى إلى sufferparkafterFailedAcquire.
هذه المرة ، لأن waitstatus في الرأس هو node.signal ، يجب أن يعود usparkafterfailedacquire.
هذه المرة أحتاج إلى رؤية طريقة ParkandCheckInterrupt.
Private Boolean ParkandCheckinterrupt () {locksupport.park (this) ؛ return thread.Interrupted () ؛ }حسنًا ، لم يتم مقاطعة الخيط ، لذا ، العودة كاذبة. تابع الحلقة في (؛ ؛) ؛).
إذا كانت الحالة دائمًا أكبر من 0 ولم يتم مقاطعة الخيط ، فسيكون دائمًا في هذه الحلقة. أي أن الحكام المذكورة في المقالة السابقة كانوا دائمًا مترددين في الإعلان عن نهاية اللعبة.
لذا تحت أي ظروف ستندلع الحلقة؟ هذا هو ، تحت أي ظروف ستكون الدولة أقل من 0؟ سأشرح المقال التالي.
لتلخيص ، فإن طريقة AWAIT () هي في الواقع تهيئة قائمة انتظار ، وإضافة مؤشر الترابط الذي يجب انتظاره (الحالة> 0) إلى قائمة انتظار ، واستخدام WaitStatus لتمييز حالة مؤشر ترابط العقدة الخلف.
ما سبق هو كل محتوى هذه المقالة. آمل أن يكون ذلك مفيدًا لتعلم الجميع وآمل أن يدعم الجميع wulin.com أكثر.