مقدمة
بالنسبة إلى InterruptedException ، فإن طريقة شائعة للتعامل معها هي "ابتلاعها" - التقاطها ولا تفعل شيئًا (أو تسجيله ، لكن هذا ليس أفضل بكثير) - تمامًا مثل الإدراج 4 لاحقًا. لسوء الحظ ، يتجاهل هذا النهج حقيقة أن المقاطعات قد تحدث خلال هذه الفترة ، وقد تتسبب المقاطعات في فقدان القدرة على إلغاء النشاط أو إغلاقه في الوقت المناسب.
طريقة الحظر
عندما يلقي طريقة ما مقاطعًا ، لا يخبرك فقط أنه يمكن أن يلقي استثناء فحص محدد ، ولكنه يخبرك أيضًا بشيء آخر. على سبيل المثال ، يخبرك أنها طريقة حظر ، والتي ستحاول التخلص من الحجب والعودة في أقرب وقت ممكن إذا كنت ترد بشكل صحيح.
تختلف طريقة الحظر عن الطريقة العامة التي تستغرق وقتًا طويلاً لتشغيلها. يعتمد الانتهاء من الطريقة العامة فقط على ما تفعله وما إذا كانت هناك موارد حوسبة كافية متاحة (دورات وحدة المعالجة المركزية والذاكرة). يعتمد إكمال طريقة الحجب أيضًا على بعض الأحداث الخارجية ، مثل انتهاء صلاحية المؤقت ، أو إكمال الإدخال/الإخراج ، أو إجراءات مؤشر ترابط آخر (حرر قفل ، أو تعيين علامة ، أو وضع مهمة في قائمة انتظار العمل). تنتهي الأساليب العامة بعد تنفيذ عملهم ، في حين يصعب التنبؤ بأساليب الحظر لأنها تعتمد على الأحداث الخارجية. يمكن أن تؤثر أساليب حظر الاستجابة لأنه من الصعب التنبؤ بموعد انتهائها.
قد لا يتم إنهاء طريقة الحظر لأنها لا يمكن أن تنتظر الحدث الذي ينتظره ، لذلك من المفيد جدًا جعل طريقة الحظر قابلة للإلغاء (وغالبًا ما تكون مفيدة للغاية إذا كانت طرق عدم الحظر الطويلة قابلة للإلغاء). تشير العملية القابلة للإلغاء إلى عملية يمكن إنهاءها من الخارج قبل الانتهاء العادي. آلية المقاطعة التي يوفرها مؤشر ترابط ودعمها Thread.sleep () و Object.wait () هي آلية إلغاء ؛ يسمح لخيط واحد لطلب مؤشر ترابط آخر لإيقاف ما يفعله. عندما يلقي طريقة ما مقاطعًا ، فإنها تخبرك أنه إذا تم مقاطعة مؤشر الترابط الذي ينفذ الطريقة ، فسوف يحاول إيقاف ما يفعله ويعود مسبقًا ، وعن طريق رمي مقاطع المقاطع ، فإنه يعود مسبقًا. يجب أن تستجيب طريقة مكتبة الحظر المخصصة للمقاطعات ورمي مقاطع المقاطع بحيث يمكن استخدامها في أنشطة قابلة للإلغاء دون التأثير على الاستجابة.
خيط مقاطعة
كل مؤشر ترابط له سمة منطقية مرتبطة بها ، والتي تمثل الحالة المقطوعة من الخيط. حالة المقاطعة خاطئة في البداية ؛ عندما يقطع مؤشر ترابط آخر مؤشر ترابط عن طريق الاتصال. إذا كان هذا الخيط ينفذ طريقة حظر قابلة للمقاطعة ذات المستوى المنخفض ، مثل thread.sleep () أو thread.join () أو object.wait () ، فإنه سيؤدي إلى إلغاء حظره ورميه. خلاف ذلك ، فإن المقاطعة () فقط يعين حالة المقاطعة للخيط. بعد أن يمكن للرمز الذي يعمل في مؤشر الترابط المقاطع استطلاع حالة المقاطعة لمعرفة ما إذا كان يُطلب منه إيقاف ما تفعله. يمكن قراءة حالة المقاطعة بواسطة thread.isinterrupted () ويمكن قراءتها ومسحها بواسطة عملية تسمى Thread.Interrupted ().
الانقطاع هو آلية تعاونية. عندما يقطع مؤشر ترابط مؤشر ترابط آخر ، لا يوقف الخيط المقاطع بالضرورة ما تفعله على الفور. بدلاً من ذلك ، فإن المقاطعة هي طلب مهذب لخيط آخر لوقف ما يفعله عندما يكون على استعداد ومريح. بعض الطرق ، مثل thread.sleep () ، تأخذ مثل هذه الطلبات على محمل الجد ، ولكن كل طريقة لا تستجيب بالضرورة للمقاطعات. بالنسبة لطلبات المقاطعة ، يمكن للأساليب التي لا تحظر ولكن لا تزال تستغرق وقتًا طويلاً لتنفيذها يمكن استطلاع حالة المقاطعة والعودة مسبقًا عند مقاطعة. يمكنك تجاهل طلبات المقاطعة في الإرادة ، لكن القيام بذلك سيؤثر على الاستجابة.
تتمثل إحدى فوائد الطبيعة التعاونية للانقطاع في أنها توفر مرونة أكبر لبناء أنشطة قابلة للإلغاء بأمان. نادراً ما نريد أن يتوقف نشاط على الفور ؛ إذا تم إلغاء النشاط أثناء تحديث التحديث ، فقد يكون بنية بيانات البرنامج غير متسقة. تتيح المقاطعة نشاطًا قابل للإلغاء لتنظيف العمل المستمر ، واستعادة الثوابات ، وإخطار الأنشطة الأخرى بأنه يجب إلغاؤه قبل إنهاءه.
التعامل مع InterruptedException
إذا كان رمي مقاطع المقاطع يعني أن الطريقة هي طريقة حظر ، فإن استدعاء طريقة الحظر يعني أن طريقتك هي أيضًا طريقة حظر ، ويجب أن يكون لديك نوع من الإستراتيجية للتعامل مع InterruptedException. عادةً ما تكون أسهل استراتيجية هي رمي مقاطع المقاطعة بنفسك ، كما هو موضح في الكود في أساليب Puttask () و GetTask () في القائمة 1. القيام بذلك يجعل الطريقة تستجيب للمقاطعات وإضافة ببساطة مقاطع إلى جملة Risrows.
قائمة 1. عدم الالتزام بالمقاطعات ، ونشره على المتصل
الفئة العامة taskqueue {private static final int max_tasks = 1000 ؛ plockingqueue plockingqueue الخاص <Task> Queue = New LinkedBlockingQueue <Stage> (max_tasks) ؛ Public void puttask (Task R) يلقي InterruptedException {queue.put (r) ؛ } المهمة العامة getTask () رميات interruptedException {return queue.take () ؛ }} في بعض الأحيان يكون هناك حاجة إلى بعض أعمال التنظيف قبل نشر الاستثناء. في هذه الحالة ، يمكنك التقاط المقاطعات ، وإجراء عملية تنظيف ، ثم رمي استثناء. توضح قائمة 2 هذه التقنية ، وهي آلية تستخدم لمطابقة اللاعبين في خدمات الألعاب عبر الإنترنت. تنتظر طريقة MatchPlayers () لاعبين للوصول ثم تبدأ لعبة جديدة. إذا تمت مقاطعة الطريقة عندما يصل لاعب واحد ولكن لم يصل لاعب آخر ، فسيعيد ذلك اللاعب إلى قائمة الانتظار وإعادة إمكانية إعادة التهوية حتى لا يضيع طلب اللاعب إلى اللعبة.
قائمة 2. أداء أعمال التنظيف الخاصة بالمهمة قبل إعادة التهديد
فئة عامة PlayerMatcher {Private Playersource Players ؛ playermatcher العامة (لاعبي playersource) {this.players = players ؛ } public void matchplayers () remrows interruptedException {try {player playerOne ، playertwo ؛ بينما (صحيح) {playerOne = playertwo = null ؛ // انتظر وصول اثنين من اللاعبين وبدء لاعب لعبة جديد = players.waitforplayer () ؛ // يمكن أن يرمي أي playertwo = players.waitforplayer () ؛ // يمكن أن يرمي أي startNewGame (playerOne ، playertwo) ؛ }} catch (interruptedException e) {// إذا حصلنا على لاعب واحد وتم مقاطعة ، ضع هذا اللاعب مرة أخرى إذا (playerOne! = null) players.addfirst (playerOne) ؛ // ثم نشر استثناء رمي e ؛ }}} لا تتوقف عن البلع على قيد الحياة
في بعض الأحيان ، لا يكون رمي مقاطع المقاطع مناسبًا ، على سبيل المثال ، عندما تستدعي المهمة التي يتم تحديدها بواسطة تشغيل طريقة قابلة للتشغيل طريقة قابلة للمقاطعة. في هذه الحالة ، لا يمكن إعادة تشكيل المقاطعات ، لكنك لا تريد أن تفعل شيئًا أيضًا. عندما تكتشف طريقة الحظر المقاطعة ويرمي مقاطعًا ، فإنه يمسح حالة المقاطعة. إذا تم القبض على interruptedException ولكن لا يمكن إعادة تربيته ، فيجب أن يتم الاحتفاظ بالدليل على المقاطعة بحيث يمكن أن يعرف رمز المستوى الأعلى في مكدس المكالمات المقاطعة والرد عليها. يمكن القيام بهذه المهمة عن طريق استدعاء Interrupt () "لإعادة تقطيع" مؤشر الترابط الحالي "، كما هو موضح في القائمة 3. على الأقل ، كلما تم اكتشاف مقاطع المقاطعة ولم يتم إعادة تراكمها ، يتم إعادة تقطيع الخيط الحالي قبل العودة.
قائمة 3. استئناف الحالة المتقطعة بعد التقاط المقاطعات
الطبقة العامة TaskRunner تنفذ RunNable {private blockingqueue <sugff> قائمة انتظار ؛ TaskRunner العام (قائمة انتظار blockingqueue <Task>) {this.queue = Queue ؛ } public void run () {try {when (true) {task task = queue.take (10 ، timeUnit.Seconds) ؛ Task.execute () ؛ }} catch (interruptedException e) {// استعادة مؤشر ترابط الحالة المقاطع. }}} أسوأ شيء يجب القيام به عند التعامل مع InterruptedException هو ابتلاعه الخام-ثم لا يعيد إدخاله أو إعادة تقييم حالة المقاطعة للخيط. للحصول على استثناء أنك لا تعرف كيفية التعامل معها ، فإن الطريقة الأكثر قياسية للتعامل معها هي التقاطها وتسجيلها ، ولكن هذه الطريقة لا تزال مثل مقاطعة خام ، لأن التعليمات البرمجية ذات المستوى الأعلى في مكدس المكالمات لا يزال لا يمكن الحصول على معلومات حول الاستثناء. (ليس من الحكمة تسجيل InterrupruptedException ، لأنه قد فات الأوان لمعالجته عندما يأتي شخص ما لقراءة السجل.) تعرض القائمة 4 نمطًا مستخدمًا على نطاق واسع ، وهو أيضًا نمط من انقطاع البلع الخام:
قائمة 4. انقطاع البلع الخام - لا تفعل هذا
// لا تفعل هذه الطبقة العامة TaskRunner تنفذ Runnable {private blockingqueue <Task> قائمة انتظار ؛ TaskRunner العام (قائمة انتظار blockingqueue <Task>) {this.queue = Queue ؛ } public void run () {try {when (true) {task task = queue.take (10 ، timeUnit.Seconds) ؛ Task.execute () ؛ }} catch (ابتلع InterruptedException) { / * لا تفعل هذا - استعادة الحالة المقطوعة بدلاً من ذلك * /}}} إذا كان لا يمكن إعادة إدخال مقاطعات ، بغض النظر عما إذا كنت تخطط لمعالجة طلب المقاطعة أم لا ، فلا تزال بحاجة إلى إعادة تقطيع مؤشر الترابط الحالي ، حيث قد يكون لطلب مقاطعة واحد "مستقبلات" متعددة. إن تنفيذ مؤشر ترابط سلسلة مؤشرات الترابط القياسية (ThreadPoolExecutor) مسؤول عن الانقطاع ، لذلك يمكن أن يكون لمقاطعة المهمة في تجمع مؤشرات الترابط الجري تأثير مزدوج. أحدهما هو إلغاء المهمة ، والآخر هو إخطار مؤشر ترابط التنفيذ بأن تجمع مؤشرات الترابط على وشك إغلاقه. إذا كانت المهمة تلتهم الطلب ، فلن يعلم مؤشر ترابط العامل أن هناك مقاطعة مطلوبة ، مما يؤدي إلى تأخير إيقاف التطبيق أو الخدمة.
تنفيذ المهام الملغاة
لا توجد دلالات محددة للمقاطعات في مواصفات اللغة ، ولكن في البرامج الأكبر ، من الصعب الحفاظ على أي دلالات مقاطعة باستثناء الإلغاء. اعتمادًا على النشاط ، يمكن للمستخدمين طلب الإلغاء من خلال واجهة المستخدم الرسومية أو من خلال آلية شبكة ، مثل JMX أو خدمة الويب. يمكن أيضًا طلب منطق البرنامج إلغاء. على سبيل المثال ، سيتم إغلاق زاحف الويب تلقائيًا إذا اكتشف أن القرص ممتلئ ، وإلا فإن الخوارزمية المتوازية ستبدأ عدة مؤشرات ترابط للبحث عن مناطق مختلفة من مساحة الحل ، وإلغاء هذه الخيوط بمجرد أن يجد أحد المواضيع حلًا.
لمجرد أن المهمة قابلة للإلغاء لا تعني أن طلب المقاطعة يجب أن يتم الرد على الفور. بالنسبة للمهام التي تنفذ الكود في حلقة ، عادة ما يكون من الضروري فقط التحقق من المقاطعات مرة واحدة لكل تكرار حلقة. اعتمادًا على المدة التي يتم فيها تنفيذ الحلقة ، قد يستغرق الأمر بعض الوقت حتى يلاحظ أي رمز أن مؤشر الترابط قد توقف (إما استطلاع حالة المقاطعة عن طريق استدعاء thread.isinterred () أو استدعاء طريقة الحظر). إذا كانت المهمة تحتاج إلى استجابة ، فيمكنها استطلاع حالة المقاطعة بشكل متكرر. عادةً ما تقوم طريقة الحجب بالاستطلاعات على حالة المقاطعة فورًا في المدخل ، وإذا تم تعيينها لتحسين الاستجابة ، فإنها ترمي أيضًا مقاطعًا.
المرة الوحيدة التي يمكنك من خلالها التوقف عن البلع على قيد الحياة هي أنك تعرف أن الخيط على وشك الخروج. يحدث هذا السيناريو فقط عندما يكون الفئة التي تستدعي الطريقة القابلة للمقاطعات جزءًا من مؤشر ترابط ، غير قابلة للتشغيل أو رمز المكتبة العامة ، كما هو موضح في القائمة 5. القائمة 5 تنشئ سلسلة رسائل يسرد الأرقام الأولية حتى يتم مقاطعة ، والتي يُسمح أيضًا للخروج عند مقاطعة. الحلقة المستخدمة للبحث عن الأعداد الأولية تتخلى عن الانقطاعات في مكانين: أحدهما هو استطلاع طريقة ISInterrupted () على رأس الحلقة ، والآخر هو استدعاء طريقة الحظر loblockqueue.put ().
قائمة 5. إذا كنت تعلم أن الخيط على وشك الخروج ، يمكنك ابتلاع المقاطعة
يمتد فئة PrimeProducer Public Thread {private Final Blockingqueue <Biginteger> قائمة انتظار ؛ PrimeProducer (plockingqueue <Queinteger> Queue) {this.queue = Queue ؛ } public void run () {try {biginteger p = biginteger.one ؛ بينما (! thread.currentTherD () } catch (interruptedException المستهلك) { / * السماح للموضوع بالخروج * /}} public void cancel () {interrupt () ؛ }} طريقة حظر غير قابلة للكسر
ليس كل أساليب الحظر رمي المقاطع. حظر فئات دفق الإدخال والمخرجات في انتظار إكمال I/O ، لكنها لا ترمي المقاطع ولن تعود مقدمًا في حالة توقفها. ومع ذلك ، بالنسبة إلى المقبس I/O ، إذا أغلق مؤشر ترابط المقبس ، فإن عملية الإدخال/الإخراج الحظر على هذا المقبس ستنتهي مبكرًا وإلقاء SocketException. لا تدعم فصول I/O غير المحظورة في Java.nio I/O المقاطعة ، ولكن يمكن أيضًا إلغاء حظرها عن طريق إغلاق القناة أو طلب الاستيقاظ على المحدد. وبالمثل ، لا يمكن مقاطعة محاولة الحصول على قفل داخلي (إدخال كتلة متزامنة) ، لكن إعادة إدخالها يدعم وضع الاستحواذ القابل للمقاطع.
مهام لا يمكن أن تكون
بعض المهام ترفض المقاطعة ، مما يجعلها غير مخصصة. ومع ذلك ، حتى المهام غير القابلة للإلغاء يجب أن تحاول الحفاظ على حالة المقاطعة في حالة حاجة رمز المستوى الأعلى على مكدس المكالمات إلى معالجة المقاطعات بعد انتهاء المهام غير القابلة للسرطان. تعرض القائمة 6 طريقة تنتظر قائمة انتظار الحظر حتى يظهر عنصر متاح في قائمة الانتظار ، بغض النظر عما إذا كانت قد توقفت أم لا. للراحة ، يستأنف حالة المقاطعة بعد النهاية في كتلة أخيرًا ، حتى لا تحرم المتصل من طلب المقاطعة. (لا يمكن استئناف حالة المقاطعة في وقت مبكر ، لأن ذلك سيؤدي إلى حلقة لا حصر لها - ستقوم blockingqueue.take () على الفور بالاستطلاع على حالة المقاطعة عند المدخل ، وإذا تم العثور على مجموعة حالة المقاطعة ، فسيتم إلقاء مقاطع.)
سرد 6. المهام التي لا يمكن الإسلام التي استعدت الحالة المتقطعة قبل العودة
المهمة العامة getNextTask (plockingqueue <quipe> قائمة انتظار) {Boolean interrupt = false ؛ حاول {بينما (صحيح) {try {return queue.take () ؛ } catch (interruptedException e) {interrupt = true ؛ ] }} لخص
يمكنك استخدام آلية مقاطعة التعاون التي توفرها منصة Java لبناء استراتيجيات الإلغاء المرنة. يمكن أن تقرر الأنشطة وفقًا لتقديرها ما إذا كانت قابلة للإلغاء أو غير قابلة للالتقاط ، وكيفية الاستجابة للمقاطعات ، ويمكنها أيضًا تأجيل المقاطعات إذا كان العائد الفوري سيؤدي إلى تسوية سلامة التطبيق. حتى إذا كنت ترغب في تجاهل المقاطعات بالكامل في الكود الخاص بك ، فيجب عليك التأكد من استعادة حالة المقاطعة دون إعادة تقديمها ، بحيث لا يعرف الرمز الذي تسميه ما هي المقاطعة. ما سبق هو المحتوى الكامل لنظرية Java وممارسة التعامل مع استثناءات InterruptedException. آمل أن يكون هذا المقال مفيدًا لك. إذا كان لديك أي أسئلة ، فيرجى ترك رسالة للمناقشة.