في هذا الفصل ، سنقدم طريقة انتظار الخيط/الاستيقاظ. يتضمن المحتوى المعني:
1.
2. انتظر () وأبلغ ()
3. انتظر (مهلة طويلة) وأخطر ()
4. انتظر () وأخطئ () ()
5. لماذا يتم إخطار () ، وانتظر () والوظائف الأخرى المحددة في الكائن ، وليس مؤشر الترابط
مقدمة في Wait () ، إخطار () ، إخطار () وطرق أخرى
في Object.java ، يتم تعريف واجهات مثل Wait () ، و Amply () وإخطار (). تتمثل وظيفة WAIT () في السماح لخيط الخيط الحالي بإدخال حالة انتظار ، وسيسمح WAIT () أيضًا بتصدر مؤشر الترابط الحالي القفل الذي يحتفظ به. يتمثل دور الإخطار () وإخطار () في إيقاظ سلسلة الانتظار على الكائن الحالي ؛
تفاصيل API حول الانتظار/الاستيقاظ في فئة الكائن هي كما يلي:
إخطار () - استيقظ مؤشر ترابط واحد ينتظر على شاشة الكائن هذا.
الإخطار () - استيقظ على جميع المواضيع التي تنتظر شاشة الكائن هذا.
انتظر () - ضع مؤشر الترابط الحالي في حالة "انتظار (حظر)" و "حتى تتصل مؤشرات الترابط الأخرى بطريقة الإخطار () أو طريقة الإخطار () لهذا الكائن" ، ويتم إيقاظ مؤشر الترابط الحالي (يتم إدخاله إلى " حالة جاهزة ").
انتظر (فترة طويلة) - دع الخيط الحالي يكون في حالة "انتظار (حظر)" و "حتى تتصل مؤشرات الترابط الأخرى بطريقة الإخطار () أو طريقة الإخطار () لهذا الكائن ، أو تتجاوز مقدار الوقت المحدد" ، ويتم إيقاظ الموضوع الحالي (أدخل "جاهز").
انتظر (int timeout ، int nanos) - ضع الخيط الحالي في "حالة انتظار (حظر)" حتى يقوم مؤشر ترابط آخر باستدعاء طريقة الإخطار () أو تجاوز مقدار الوقت الفعلي "، ويتم إيقاظ الخيط الحالي (تم إدخاله إلى" حالة جاهزة ").
2. انتظر () وإخطار () أمثلة
فيما يلي مثال لإظهار "WAIT () وإخطار () معًا".
نسخة الكود كما يلي:
// waittest.java كود المصدر
يمتد الفئة Threada Thread {
public threada (اسم السلسلة) {
سوبر (الاسم) ؛
}
تشغيل الفراغ العام () {
متزامن (هذا) {
System.out.println (thread.currentThRead (). getName ()+"Call Notify ()") ؛
// استيقظ موضوع الانتظار الحالي
إخطار () ؛
}
}
}
فئة عامة waittest {
الفراغ الثابت العام الرئيسي (سلسلة [] args) {
threada t1 = new threada ("t1") ؛
متزامن (T1) {
يحاول {
// ابدأ "الموضوع T1"
system.out.println (thread.currentThRead (). getName ()+"start t1") ؛
t1.start () ؛
// ينتظر الخيط الرئيسي T1 للاستيقاظ من خلال الإخطار ().
System.out.println (thread.currentThRead (). getName ()+"wait ()") ؛
T1.WAIT () ؛
System.out.println (thread.currentThRead (). getName ()+"متابعة") ؛
} catch (InterruptedException e) {
E.PrintStackTrace () ؛
}
}
}
}
نتائج التشغيل:
نسخة الكود كما يلي:
البداية الرئيسية T1
الانتظار الرئيسي ()
T1 استدعاء الإخطار ()
المتابعة الرئيسية
وصف النتائج:
يوضح الشكل التالي تدفق "الخيط الرئيسي" و "Thread T1".
(01) لاحظ أن "الخيط الرئيسي" في الشكل يمثل "مؤشر الترابط الرئيسي الرئيسي". "الموضوع T1" يمثل "الموضوع T1" بدأ في Waittest. و "القفل" يمثل "قفل متزامن للكائن T1".
(02) "الخيط الرئيسي" ينشئ "موضوع T1" جديد من خلال Threada جديد ("T1"). ثم ، يتم الحصول على "القفل المتزامن لكائن T1" من خلال متزامن (T1). ثم اتصل بـ T1.start () لبدء "Thread T1".
(03) ينفذ "مؤشر الترابط الرئيسي" T1.WAIT () لإطلاق "قفل كائن T1" ويدخل "Wait (حظر) الحالة". انتظر المواضيع على كائنات T1 لتستيقظها عبر إخطار () أو إخطار ().
(04) بعد تشغيل "Thread T1" ، يتم الحصول على "قفل الكائن الحالي" من خلال متزامن (هذا) ؛ "الموضوع الرئيسي".
(05) بعد اكتمال "Thread T1" ، حرر "قفل الكائن الحالي". بعد ذلك مباشرة ، يكتسب "الخيط الرئيسي" "قفل كائن T1" ثم يعمل.
للرمز أعلاه؟ سأل أحد الأصدقاء ذات مرة: T1.WAIT () يجب أن تجعل "Thread T1" الانتظار ؛
قبل الإجابة على هذا السؤال ، دعونا نلقي نظرة على فقرة حول الانتظار في مستند JDK:
نسخة الكود كما يلي:
يتسبب في انتظار مؤشر الترابط الحالي حتى يستدعي مؤشر ترابط آخر طريقة الإخطار () أو طريقة الإخطار () لهذا الكائن.
بمعنى آخر ، تتصرف هذه الطريقة تمامًا كما لو أنها تنفذ الاتصال (0).
يجب أن يمتلك الخيط الحالي شاشة هذا الكائن. يمكن إعادة إغلاق ملكية الشاشة واستئناف التنفيذ.
المعنى باللغة الصينية تقريبًا:
يتسبب في انتظار "الخيط الحالي" حتى يقوم مؤشر ترابط آخر بإخطار () أو إخطار () بإيقاظ الخيط. وبعبارة أخرى ، هذه الطريقة لها نفس تأثير الانتظار (0)! (تكميلي ، لأسلوب الانتظار (Millis Long Millis) ، عندما يكون Millis 0 ، فهذا يعني الانتظار اللانهائي حتى يتم استيقاظه بواسطة إخطار () أو إخطار ()).
عندما تكون مكالمات "الخيط الحالي" WAIT () ، يجب أن يكون لها قفل المزامنة للكائن. بعد استدعاء الخيط () ، سيتم إصدار القفل ؛ ثم يستمر مؤشر الترابط في الانتظار حتى يعادل "قفل المزامنة لهذا الكائن" ومن ثم يمكنه الاستمرار في التشغيل.
ملاحظة: في شرح JDK ، يقال أن وظيفة Wait () هي جعل "الخيط الحالي" الانتظار ، ويشير "الخيط الحالي" إلى مؤشر الترابط الذي يعمل على وحدة المعالجة المركزية!
هذا يعني أيضًا أنه على الرغم من أن t1.wait () هي طريقة wait () تسمى من خلال "Thread T1" ، فإن المكان الذي يطلق عليه t1.wait () في "Main Thread Main". يجب أن يكون الخيط الرئيسي هو "الخيط الحالي" ، أي حالة التشغيل ، قبل تنفيذ t1.wait (). لذلك ، "الخيط الحالي" في هذا الوقت هو "الموضوع الرئيسي الرئيسي"! لذلك ، T1.WAIT () هو جعل "الخيط الرئيسي" انتظر ، وليس "الموضوع T1"!
3. انتظر (مهلة طويلة) وأخطر ()
انتظر (and timeout) سيضع الخيط الحالي في حالة "انتظار (حظر)" و "حتى تتصل مؤشرات الترابط الأخرى بطريقة الإخطار () يتم إيقاظ الموضوع الحالي (إدخال) "جاهز").
يوضح المثال التالي مهلة الانتظار (المهلة الطويلة) ، ويتم إيقاظ الموضوع.
نسخة الكود كما يلي:
// كود Waittimeouttest.java
يمتد الفئة Threada Thread {
public threada (اسم السلسلة) {
سوبر (الاسم) ؛
}
تشغيل الفراغ العام () {
System.out.println (thread.currentThRead (). getName () + "run") ؛
// دورة مفرغة ، تعمل بشكل مستمر.
بينما (صحيح)
}
}
الطبقة العامة waittimeouttest {
الفراغ الثابت العام الرئيسي (سلسلة [] args) {
threada t1 = new threada ("t1") ؛
متزامن (T1) {
يحاول {
// ابدأ "الموضوع T1"
system.out.println (thread.currentThRead (). getName () + "start t1") ؛
t1.start () ؛
// ينتظر الخيط الرئيسي T1 من خلال الإخطار () أو الإخطار () ، أو التأخير الذي يتجاوز 3000 مللي ثانية ؛
System.out.println (thread.currentThRead (). getName () + "Call Wait") ؛
T1.WAIT (3000) ؛
System.out.println (thread.currentThRead (). getName () + "متابعة") ؛
} catch (InterruptedException e) {
E.PrintStackTrace () ؛
}
}
}
}
نتائج التشغيل:
نسخة الكود كما يلي:
البداية الرئيسية T1
المكالمة الرئيسية انتظر
T1 Run // بعد حوالي 3 ثوانٍ ... الإخراج "Main Contens"
المتابعة الرئيسية
وصف النتائج:
يوضح الشكل التالي تدفق "الخيط الرئيسي" و "Thread T1".
(01) لاحظ أن "الخيط الرئيسي" في الشكل يمثل مؤشر ترابط WaittimeOutTest الرئيسي (على سبيل المثال ، مؤشر الترابط الرئيسي). "الموضوع T1" يمثل الموضوع T1 بدأ في Waittest. و "القفل" يمثل "قفل متزامن للكائن T1".
(02) يقوم مؤشر الترابط الرئيسي بتنفيذ T1.Start () لبدء "Thread T1".
(03) ينفذ مؤشر الترابط الرئيسي الرئيسي T1.WAIT (3000) ، وفي هذا الوقت ، يدخل الخيط الرئيسي "حالة الحظر". من الضروري أن يكون "مؤشر الترابط المستخدم لقفل كائن T1 لإيقاظه من خلال الإخطار () أو الإخطار ()" أو "بعد 3000MS Timeout" ، يدخل مؤشر الترابط الرئيسي "الحالة الجاهزة" ثم يمكن تشغيله.
(04) بعد تشغيل "Thread T1" ، يدخل حلقة ميتة ويستمر في التشغيل.
(05) بعد أن يكون المهلة 3000 مللي ثانية ، سيدخل الخيط الرئيسي الرئيسي "الحالة الجاهزة" ثم أدخل "حالة الجري".
4. انتظر () وأخطئ () ()
من خلال المثال السابق ، نعلم أن الإخطار () يمكن أن يستيقظ مؤشر ترابط واحد ينتظر على شاشة الكائن هذا.
أدناه ، نوضح استخدام الإخطار () من خلال مثال ؛
نسخة الكود كما يلي:
الطبقة العامة إعلام {
كائن ثابت خاص obj = كائن جديد () ؛
الفراغ الثابت العام الرئيسي (سلسلة [] args) {
threada t1 = new threada ("t1") ؛
threada t2 = new threada ("t2") ؛
threada t3 = new threada ("t3") ؛
t1.start () ؛
t2.start () ؛
t3.start () ؛
يحاول {
System.out.println (thread.currentThRead (). getName ()+"Sleep (3000)") ؛
thread.sleep (3000) ؛
} catch (InterruptedException e) {
E.PrintStackTrace () ؛
}
متزامن (OBJ) {
// الخيط الرئيسي ينتظر الاستيقاظ.
System.out.println (thread.currentThRead ().
obj.notifyall () ؛
}
}
يمتد Threada الفئة الثابتة الموضوع {
public threada (اسم السلسلة) {
سوبر (الاسم) ؛
}
تشغيل الفراغ العام () {
متزامن (OBJ) {
يحاول {
// نتيجة إخراج الطباعة
system.out.println (thread.currentThRead (). getName () + "wait") ؛
// استيقظ موضوع الانتظار الحالي
obj.wait () ؛
// نتيجة إخراج الطباعة
System.out.println (thread.currentThRead (). getName () + "متابعة") ؛
} catch (InterruptedException e) {
E.PrintStackTrace () ؛
}
}
}
}
}
نتائج التشغيل:
نسخة الكود كما يلي:
T1 انتظر
النوم الرئيسي (3000)
T3 انتظر
T2 انتظر
الإخطار الرئيسي ()
T2 تستمر
T3 تستمر
T1 تستمر
وصف النتائج:
الرجوع إلى المخطط الانسيابي أدناه.
(01) تم إنشاء المواضيع 3 "T1" و "T2" و "T3" وبدأت في الخيط الرئيسي.
(02) ينام الخيط الرئيسي لمدة 3 ثوان من خلال النوم (3000). خلال نوم الخيط الرئيسي لمدة 3 ثوان ، نفترض أن الخيوط الثلاثة "T1" و "T2" و "T3" تعمل كلها. خذ "T1" كمثال. سوف تنتظر أيضًا أن توقظ المواضيع الأخرى منها من خلال nofity () أو nofityall ().
(03) ينام الخيط الرئيسي لمدة 3 ثوان ثم يعمل. قم بتنفيذ OBJ.Notifyall () لإيقاظ موضوع الانتظار على OBJ ، أي ، استيقظ الخيوط الثلاثة "T1" و "T2" و "T3". مباشرة بعد تشغيل متزامن الخيط الرئيسي (OBJ) ، يطلق الخيط الرئيسي "قفل OBJ". وبهذه الطريقة ، يمكن "T1" و "T2" و "T3" الحصول على "قفل OBJ" والاستمرار في الجري!
5. لماذا يتم إخطار () ، وانتظر () والوظائف الأخرى المحددة في الكائن ، وليس مؤشر الترابط
ستعمل وظائف مثل WAIT () ، الإخطار () في الكائن ، مثل المزامنة ، على "قفل مزامنة الكائن".
سوف ينتظر () انتظر "الخيط الحالي". لا تكون قادرًا على الجري!
حسنًا ، بعد استدعاءات الخيط () ، ستصدر "القفل المتزامن" الذي تم وضعه بواسطة قفله ؛ الآن ، يرجى التفكير في سؤال: ما هو الإخطار () بناءً على إيقاظ موضوع الانتظار؟ أو ، ما هو العلاقة بين Wait () وإخطار ()؟ الجواب هو: بناءً على "قفل مزامنة الكائن".
الخيط المسؤول عن إيقاظ مؤشر ترابط الانتظار (نسميه "خيط الاستيقاظ") ، فإنه يحصل فقط على "قفل المزامنة للكائن" (يجب أن يكون قفل المزامنة هنا هو نفس قفل التزامن لخيط الانتظار) والمكالمات الإخطار () أو بعد طريقة الإخطار () ، يمكن إيقاظ مؤشر ترابط الانتظار. على الرغم من أن موضوع الانتظار يستيقظ ؛ يجب أن تنتظر حتى يطلق مؤشر ترابط الاستيقاظ "قفل مزامنة الكائن" قبل أن تتمكن من الحصول على "قفل مزامنة الكائن" والاستمرار في التشغيل.
باختصار ، يعتمد الإخطار () ، WAIT () على "قفل متزامن" ، والذي يتم الاحتفاظ به بواسطة أقفال الكائن ، وكل كائن لديه واحد فقط! هذا هو السبب في تعريف وظائف مثل الإخطار () ، WAIT () في فئة الكائن ، وليس في فئة مؤشرات الترابط.