-
نظرًا لأن سلاسل العمليات المتعددة لنفس العملية تشترك في نفس مساحة التخزين، فإنها توفر الراحة، ولكنها تسبب أيضًا مشكلة خطيرة تتمثل في تعارض الوصول. توفر لغة Java آلية خاصة لحل هذا التعارض، مما يمنع بشكل فعال الوصول إلى نفس كائن البيانات بواسطة سلاسل رسائل متعددة في نفس الوقت.
نظرًا لأنه يمكننا استخدام الكلمة الأساسية الخاصة للتأكد من أنه لا يمكن الوصول إلى كائن البيانات إلا عن طريق الطرق، نحتاج فقط إلى اقتراح آلية للطرق. هذه الآلية هي الكلمة الأساسية المتزامنة، والتي تتضمن استخدامين: الطريقة المتزامنة والكتلة المتزامنة.
1. الطريقة المتزامنة: أعلن عن الطريقة المتزامنة عن طريق إضافة الكلمة الأساسية المتزامنة في إعلان الطريقة. يحب:
AccessVal الفارغ المتزامن العام (int newVal)؛
تتحكم الطريقة المتزامنة في الوصول إلى متغيرات أعضاء الفئة: يتوافق كل مثيل فئة مع قفل، ويجب أن تحصل كل طريقة متزامنة على قفل مثيل الفئة الذي يستدعي الطريقة قبل أن يتم تنفيذها، وإلا فسيتم حظر الخيط الذي تنتمي إليه. بمجرد تنفيذ الطريقة، ستشغل متغير عضو الفئة حصريًا، ولن يتم تحرير القفل حتى العودة من هذه الطريقة، وبعد ذلك يمكن للخيط المحظور الحصول على القفل وإعادة الدخول إلى الحالة القابلة للتنفيذ. تضمن هذه الآلية أنه بالنسبة لكل مثيل فئة في نفس الوقت، تكون إحدى وظائف الأعضاء المعلنة كمتزامنة على الأكثر في الحالة القابلة للتنفيذ (لأنه على الأكثر يمكن للمرء الحصول على القفل المطابق لمثيل الفئة) (انتبه إلى هذا البيان نظرًا لأنه قفل لمثيل فئة، لذلك بالنسبة للكائن في كل مرة، يتم تنفيذ طريقة متزامنة واحدة فقط بواسطة مؤشر ترابط واحد في كل مرة)، وبالتالي تجنب تعارض الوصول إلى متغيرات أعضاء الفئة بشكل فعال (طالما أن جميع الأساليب قد تكون كذلك!) يتم تعريف متغيرات أعضاء فئة الوصول على أنها متزامنة).
في Java، ليس فقط مثيلات الفئة، تتوافق كل فئة أيضًا مع القفل. وبهذه الطريقة، يمكننا أيضًا الإعلان عن وظائف الأعضاء الثابتة للفئة كمتزامنة للتحكم في وصولها إلى متغيرات الأعضاء الثابتة للفئة.
عيوب الطريقة المتزامنة: إذا تم الإعلان عن طريقة كبيرة على أنها متزامنة، فسوف تتأثر الكفاءة بشكل كبير. سوف يتسبب في عدم نجاح استدعاءاته لأية أساليب متزامنة من هذه الفئة. بالطبع، يمكننا حل هذه المشكلة عن طريق وضع الكود الذي يصل إلى متغيرات أعضاء الفئة في طريقة خاصة، والإعلان عنها على أنها متزامنة، واستدعائها في الطريقة الرئيسية، لكن Java توفر لنا حلاً أفضل، وهو الكتلة المتزامنة.
2. الكتلة المتزامنة: قم بإعلان الكتلة المتزامنة من خلال الكلمة الأساسية المتزامنة. بناء الجملة هو كما يلي:
متزامن (سينك أوبجيكت) {
// رمز للسماح بالتحكم في الوصول
}
الكتلة المتزامنة هي كتلة تعليمات برمجية يجب أن يحصل فيها الكود على قفل الكائن syncObject (كما ذكرنا سابقًا، يمكن أن يكون مثيلًا لفئة أو فئة) قبل أن يتم تنفيذه. الآلية المحددة هي نفسها المذكورة أعلاه. نظرًا لأنه يمكنه استهداف أي كتلة تعليمات برمجية وتحديد الكائن المقفل بشكل تعسفي، فهو يتمتع بمرونة عالية.
حظر الموضوع (المزامنة)
من أجل حل تعارضات الوصول إلى مناطق التخزين المشتركة، قدمت Java آلية مزامنة. الآن دعونا نفحص الوصول إلى الموارد المشتركة من خلال سلاسل رسائل متعددة. من الواضح أن آلية المزامنة لم تعد كافية، لأن الموارد المطلوبة في أي وقت قد لا تكون كذلك ومن أجل الوصول إليها، قد يكون هناك أكثر من مورد جاهز في نفس الوقت. لحل مشكلة التحكم في الوصول في هذه الحالة، قدمت Java دعمًا لآلية الحظر.
يشير الحظر إلى إيقاف تنفيذ سلسلة المحادثات مؤقتًا لانتظار حدوث شرط معين (مثل أن يكون المورد جاهزًا) ويجب أن يكون الطلاب الذين درسوا أنظمة التشغيل على دراية به. توفر Java عددًا كبيرًا من الأساليب لدعم الحظر، فلنحللها واحدة تلو الأخرى.
1. طريقة النوم (): تسمح لك خاصية النوم () بتحديد فترة زمنية بالمللي ثانية كمعلمة، مما يؤدي إلى دخول الخيط في حالة الحظر خلال الوقت المحدد ولا يمكنه الحصول على وقت وحدة المعالجة المركزية بمجرد مرور الوقت المحدد يعيد مؤشر الترابط الدخول إلى الحالة القابلة للتنفيذ.
عادة، يتم استخدام السكون () عند انتظار أن يكون المورد جاهزًا: بعد أن يجد الاختبار أن الشرط غير مستوفي، اترك كتلة الخيط لفترة من الوقت ثم أعد الاختبار حتى يتم استيفاء الشرط.
2. طريقتا الإيقاف المؤقت () والاستئناف () (من السهل التسبب في حالة توقف تام، وقد عفا عليهما الزمن): يتم استخدام الطريقتين معًا، مما يؤدي إلى دخول الخيط في حالة حظر ولن يتم استرداد السيرة الذاتية المقابلة له تلقائيًا تم استدعاؤه، هل يمكن للخيط إعادة الدخول إلى الحالة القابلة للتنفيذ. عادة، يتم استخدام الإيقاف المرحلي () والاستئناف () عند انتظار النتيجة التي ينتجها مؤشر ترابط آخر: بعد أن يجد الاختبار أن النتيجة لم يتم إنتاجها، يتم حظر الخيط، وبعد أن ينتج مؤشر ترابط آخر النتيجة، استدعاء السيرة الذاتية () لاستئناف ذلك.
3. طريقة العائد (): يؤدي العائد () إلى تخلي الخيط عن وقت وحدة المعالجة المركزية المخصص حاليًا، ولكنه لا يتسبب في حظر الخيط، أي أن الخيط لا يزال في حالة قابلة للتنفيذ وقد يتم تخصيص وقت وحدة المعالجة المركزية مرة أخرى في في أي وقت. إن تأثير استدعاء العائد () يعادل اعتقاد المجدول أن مؤشر الترابط قد نفذ وقتًا كافيًا للانتقال إلى مؤشر ترابط آخر.
4. طريقتا الانتظار () والإخطار (): يتم استخدام الطريقتين معًا. الانتظار () يؤدي إلى دخول الخيط في حالة الحظر، وله نموذجان يسمح أحدهما بتحديد فترة زمنية بالمللي ثانية كمعلمة لا توجد معلمات أخرى، فإن الأول سيعيد الدخول إلى الحالة القابلة للتنفيذ عند استدعاء الإخطار () المقابل أو تجاوز الوقت المحدد، ويجب استدعاء الأخير عند استدعاء الإخطار () المقابل.
للوهلة الأولى، يبدو أنهما لا يختلفان عن أزواج أسلوب الإيقاف المرحلي () والاستئناف ()، لكنهما في الواقع مختلفان تمامًا. الفرق الأساسي هو أن جميع الطرق الموضحة أعلاه لن تحرر القفل المشغول (إذا كان مشغولاً) عند الحظر، في حين أن هذه القاعدة المعاكسة هي عكس ذلك.
تؤدي الاختلافات الأساسية المذكورة أعلاه إلى سلسلة من الاختلافات التفصيلية.
بادئ ذي بدء، تنتمي جميع الطرق الموضحة أعلاه إلى فئة Thread، ولكن هذا الزوج ينتمي مباشرة إلى فئة الكائن، أي أن جميع الكائنات لديها هذا الزوج من الطرق. قد يبدو هذا أمرًا لا يصدق في البداية، ولكنه في الواقع طبيعي جدًا، لأنه عندما يتم حظر هذا الزوج من الطرق، يجب تحرير القفل المشغول، ويمتلك أي كائن القفل، مما يؤدي إلى استدعاء طريقة الانتظار () لأي كائن سيتم حظر الخيط ويتم تحرير القفل الموجود على الكائن. سيؤدي استدعاء طريقة الإخطار () لأي كائن إلى حظر مؤشر ترابط محدد عشوائيًا عن طريق استدعاء طريقة الانتظار () للكائن لإلغاء حظره (ولكن لن يتم تنفيذه حتى يتم الحصول على القفل).
ثانيًا، يمكن استدعاء جميع الطرق الموضحة أعلاه في أي مكان، ولكن يجب استدعاء هذا الزوج من الطرق (الانتظار () والإخطار ()) بطريقة متزامنة أو كتلة والسبب بسيط جدًا أيضًا، فقط في طريقة متزامنة أو كتلة فقط الخيط الحالي هو الذي يشغل القفل ويمكن تحرير القفل. بنفس الطريقة، يجب أن يكون القفل الموجود على الكائن الذي يستدعي هذا الزوج من الأساليب مملوكًا للخيط الحالي، بحيث يمكن تحرير القفل. لذلك، يجب وضع زوج من استدعاءات الطريقة في طريقة متزامنة أو كتلة يكون كائنها المقفل هو الكائن الذي يستدعي زوج الأساليب. إذا لم يتم استيفاء هذا الشرط، على الرغم من أنه لا يزال بإمكان البرنامج الترجمة، فسيحدث استثناء IllegalMonitorStateException في وقت التشغيل.
تحدد الخصائص المذكورة أعلاه لطرق الانتظار () وإخطار () أنها غالبًا ما تستخدم مع الطرق أو الكتل المتزامنة وستكشف مقارنتها بآلية الاتصال بين العمليات في نظام التشغيل عن أوجه التشابه بينهما: توفر الطرق أو الكتل المتزامنة تشابهًا. بالنسبة لوظائف أساسيات نظام التشغيل، لن يتداخل تنفيذها من خلال آلية متعددة الخيوط، وهذا النظير يعادل أساسيات الكتلة والتنبيه (يتم الإعلان عن مزامنة هذا الزوج من الأساليب). يتيح لنا الجمع بينها تنفيذ سلسلة من خوارزميات الاتصال الرائعة بين العمليات على نظام التشغيل (مثل خوارزميات الإشارة)، ويمكن استخدامها لحل العديد من مشكلات الاتصال المعقدة بين الخيوط.
نقطتان أخيرتان حول طريقتي الانتظار () والإخطار ():
أولاً: يتم اختيار الخيط غير المحظور الناتج عن استدعاء طريقة الإخطار () بشكل عشوائي من بين سلاسل الرسائل المحظورة عن طريق استدعاء طريقة الانتظار () للكائن. لا يمكننا التنبؤ بالخيط الذي سيتم تحديده، لذا كن حذرًا بشكل خاص عند البرمجة المشاكل الناجمة عن هذا عدم اليقين.
ثانيًا: بالإضافة إلى notify()، هناك أيضًا طريقة notifyAll() يمكنها أيضًا لعب دور مماثل، والفرق الوحيد هو أن استدعاء طريقة notifyAll() سيؤدي إلى إزالة جميع سلاسل الرسائل المحظورة عن طريق استدعاء طريقة wait() الخاصة بـ الكائن في وقت واحد. بالطبع، فقط الخيط الذي حصل على القفل يمكنه الدخول إلى الحالة القابلة للتنفيذ.
عندما نتحدث عن الحظر، علينا أن نتحدث عن حالة توقف تام. يمكن أن يكشف التحليل الموجز أن كلاً من طريقة التعليق () واستدعاء طريقة الانتظار () دون تحديد فترة المهلة قد يتسببان في حالة توقف تام. لسوء الحظ، جافا لا تدعم تجنب الجمود على مستوى اللغة، ويجب أن نكون حذرين لتجنب الجمود في البرمجة.
لقد قمنا أعلاه بتحليل الطرق المختلفة لحظر الخيوط في Java، وركزنا على طرق الانتظار () والإخطار () لأنها الأقوى والأكثر مرونة في الاستخدام، ولكن هذا يؤدي أيضًا إلى انخفاض كفاءتها. أكثر عرضة للخطأ. في الاستخدام الفعلي، يجب علينا استخدام أساليب مختلفة بمرونة لتحقيق أهدافنا بشكل أفضل.
موضوع الخفي
تعد سلاسل الرسائل الشيطانية نوعًا خاصًا من سلاسل الرسائل، والفرق بينها وبين سلاسل الرسائل العادية هو أنها ليست الجزء الأساسي من التطبيق سيتم الإنهاء، من ناحية أخرى، لن يتم إنهاء التطبيق طالما كان هناك مؤشر ترابط غير خفي قيد التشغيل. تُستخدم سلاسل الرسائل Daemon عمومًا لتقديم الخدمات لسلاسل الرسائل الأخرى في الخلفية.
يمكنك تحديد ما إذا كان الخيط هو خيط خفي عن طريق استدعاء الطريقة isDaemon()، أو يمكنك استدعاء الطريقة setDaemon() لتعيين الخيط كخيط خفي.
مجموعة الموضوع
مجموعة مؤشرات الترابط هي مفهوم فريد لـ Java. في Java، مجموعة مؤشرات الترابط هي كائن من فئة ThreadGroup. ينتمي كل مؤشر ترابط إلى مجموعة سلاسل رسائل فريدة عند إنشاء مؤشر الترابط ولا يمكن استخدامها طوال دورة حياة تغيير. يمكنك تحديد مجموعة مؤشرات الترابط التي ينتمي إليها مؤشر الترابط عن طريق استدعاء مُنشئ فئة مؤشر الترابط الذي يحتوي على معلمة نوع ThreadGroup. إذا لم يتم تحديده، فإن مؤشر الترابط الافتراضي هو مجموعة مؤشرات الترابط الخاصة بالنظام والتي تسمى النظام.
في Java، يجب إنشاء جميع مجموعات سلاسل الرسائل بشكل صريح باستثناء مجموعات سلاسل رسائل النظام المعدة مسبقًا. في Java، تنتمي كل مجموعة مؤشرات ترابط باستثناء مجموعة مؤشرات الترابط الخاصة بالنظام إلى مجموعة مؤشرات ترابط أخرى. يمكنك تحديد مجموعة مؤشرات الترابط التي تنتمي إليها عند إنشاء مجموعة مؤشرات الترابط إذا لم يتم تحديدها، فهي تنتمي إلى مجموعة مؤشرات الترابط الخاصة بالنظام بشكل افتراضي. بهذه الطريقة، تشكل كافة مجموعات مؤشرات الترابط شجرة تكون مجموعة مؤشرات ترابط النظام هي الجذر لها.
تسمح لنا Java بالعمل على جميع سلاسل الرسائل في مجموعة سلاسل الرسائل في نفس الوقت، على سبيل المثال، يمكننا تعيين أولوية جميع سلاسل الرسائل فيها عن طريق استدعاء الطريقة المقابلة لمجموعة سلاسل الرسائل، ويمكننا أيضًا بدء جميع سلاسل الرسائل أو حظرها. هو - هي.
هناك دور مهم آخر لآلية مجموعة سلاسل الرسائل في Java وهو سلامة السلاسل. تسمح لنا آلية مجموعة الخيوط بتمييز سلاسل الرسائل ذات خصائص أمان مختلفة من خلال التجميع، والتعامل مع سلاسل الرسائل في مجموعات مختلفة بشكل مختلف، ودعم اعتماد تدابير أمنية غير متكافئة من خلال الهيكل الهرمي لمجموعات سلاسل الرسائل. توفر فئة ThreadGroup الخاصة بـ Java عددًا كبيرًا من الطرق لتسهيل تشغيل كل مجموعة سلاسل رسائل في شجرة مجموعة سلاسل الرسائل وكل مؤشر ترابط في مجموعة سلاسل الرسائل.
حالة الخيط في وقت معين، يمكن أن يكون الخيط في حالة واحدة فقط.
جديد
المواضيع التي لم تبدأ بعد تكون في هذه الحالة.
قابل للتشغيل
المواضيع التي يتم تنفيذها في جهاز Java الظاهري تكون في هذه الحالة.
محظور
الخيط المحظور وينتظر قفل الشاشة يكون في هذه الحالة.
منتظر
الخيط الذي ينتظر إلى أجل غير مسمى حتى يقوم خيط آخر بتنفيذ عملية معينة يكون في هذه الحالة.
حالة الخيط لخيط الانتظار. مؤشر الترابط في حالة انتظار لأنه يستدعي إحدى الطرق التالية:
Object.wait بدون قيمة المهلة
Thread.join بدون قيمة المهلة
LockSupport.park
هناك مؤشر ترابط في حالة الانتظار ينتظر مؤشر ترابط آخر لتنفيذ عملية محددة. على سبيل المثال، ينتظر مؤشر الترابط الذي قام باستدعاء Object.wait() على كائن ما قيام مؤشر ترابط آخر باستدعاء Object.notify() أو Object.notifyAll() على ذلك الكائن. ينتظر مؤشر الترابط الذي يسمى Thread.join() إنهاء مؤشر الترابط المحدد.
في انتظار الوقت
الخيط الذي ينتظر خيطًا آخر لتنفيذ عملية تعتمد على وقت الانتظار المحدد يكون في هذه الحالة.
حالة مؤشر الترابط لمؤشر الترابط المنتظر مع وقت انتظار محدد. يكون مؤشر الترابط في حالة انتظار محددة بوقت عن طريق استدعاء إحدى الطرق التالية مع وقت انتظار إيجابي محدد:
الموضوع.النوم
Object.wait بقيمة المهلة
Thread.join مع قيمة المهلة
LockSupport.parkNanos
LockSupport.parkUntil
تم الإنهاء
يوجد مؤشر ترابط تم الخروج في هذه الحالة.