منذ Java 5 ، تحتوي حزمة java.util.concurrent.locks على بعض تطبيقات القفل ، لذلك لم تعد مضطرًا إلى تنفيذ الأقفال الخاصة بك بعد الآن. لكن لا تزال بحاجة إلى فهم كيفية استخدام هذه الأقفال.
قفل بسيط
لنبدأ مع كتلة التزامن في جافا:
عداد الفئة العامة {private int count = 0 ؛ public int inc () {synchronized (this) {return ++ count ؛ }}}يمكنك أن ترى أن هناك كتلة رمز متزامنة (هذا) في طريقة INC (). يمكن أن تضمن كتلة الكود هذه أن مؤشر ترابط واحد فقط يمكنه تنفيذ عدد Return ++ في نفس الوقت. على الرغم من أن الكود في كتلة المزامنة المتزامنة يمكن أن يكون أكثر تعقيدًا ، إلا أن التشغيل البسيط لعدد ++ يكفي للتعبير عن معنى مزامنة الخيط.
تستخدم فئة العداد التالية القفل بدلاً من المزامنة لتحقيق نفس الهدف:
عداد الفئة العامة {قفل خاص = قفل جديد () ؛ العد الخاص بالبيئة = 0 ؛ public int inc () {lock.lock () ؛ int newCount = ++ count ؛ lock.unlock () ؛ إرجاع NewCount ؛ }}تقوم طريقة Lock () بإغلاق كائن مثيل القفل ، لذلك سيتم حظر جميع مؤشرات الترابط التي تستدعي طريقة القفل () على الكائن حتى يتم استدعاء طريقة Unlock () لكائن القفل.
فيما يلي تطبيق بسيط لفئة القفل:
عداد الفئة العامة {قفل الفئة العامة {private boolean isClocked = false ؛ رميات void void المزامنة العامة () interruptedException {بينما (islocked) {wait () ؛ } islocked = true ؛ } unlock void inlock () {islocked = false ؛ إخطار () ؛ }}لاحظ حلقة الوقت (islocked) في الداخل ، والتي تسمى أيضًا "قفل الدوران". عندما يكون Islocked صحيحًا ، يقوم قفل استدعاء مؤشر الترابط () بقفل وينتظر على مكالمة Wait (). لمنع الموضوع من العودة من WAIT () دون تلقي مكالمة الإخطار () (تسمى أيضًا الاستيقاظ الخاطئ) ، سيعيد الخيط فحص حالة islocked لتحديد ما إذا كان يمكن أن يستمر بأمان في التنفيذ أو يحتاج إلى الاستمرار في الانتظار مرة أخرى ، بدلاً من التفكير في أن الخيط يمكن أن يستمر بأمان بعد الاستيقاظ. إذا كان Islocked خطأ ، فسيقوم مؤشر الترابط الحالي بالخروج من الحلقة (Islocked) وتعيين Islocked إلى True ، بحيث يمكن لخيوط أخرى تستدعي طريقة القفل () إضافة أقفال على مثيل القفل.
عندما يكمل مؤشر الترابط الرمز في القسم الحرج (الموجود بين القفل () و Unlock ()) ، يتم استدعاء فتح (). سيتم إعادة تنفيذ Unlock () من إعادة تعيين islocked إلى False وإخطار (الاستيقاظ) أحد المواضيع التي تسمى وظيفة Wait () في طريقة القفل () وهي في حالة انتظار.
إعادة إدخال الأقفال
كتلة المزامنة المتزامنة في Java هي إعادة إدخال. هذا يعني أنه إذا دخل مؤشر ترابط Java إلى كتلة المزامنة المتزامنة في الكود وبالتالي يحصل على قفل على الأنبوب المقابل لكائن المزامنة المستخدمة في كتلة المزامنة ، فيمكن أن يدخل مؤشر الترابط كتلة رمز Java آخر متزامن بواسطة كائن خط الأنابيب نفسه. هنا مثال:
الطبقة العامة reentrant {synchronized synchronized () {inner () ؛ } inner () {// افعل شيئًا}} العام {//لاحظ أنه يتم إعلان مزامنة كل من Outer () و Inner () ، وهو ما يعادل كتل متزامنة (هذا) في Java. إذا استدعاء مؤشر ترابط OUTER () ، فلا توجد مشكلة في الاتصال الداخلي () في OUTER () ، لأن كلتا الطريقتين (كتل الكود) تتم مزامنتها بواسطة نفس كائن الإدارة ("هذا"). إذا كان مؤشر ترابط بالفعل يحتوي على قفل على كائن أنابيب ، فإنه يمكنه الوصول إلى جميع كتل التعليمات البرمجية التي تتم مزامنتها بواسطة كائن الأنابيب. هذا هو إعادة الدخول. يمكن لخيط إدخال أي كتلة من التعليمات البرمجية متزامنة بواسطة قفل لديه بالفعل.
تطبيق القفل المذكور أعلاه ليس إعادة إدخال. إذا قمت بإعادة كتابة فئة REENTRANT مثل ما يلي ، عندما يستدعي مؤشر الترابط OUTER () ، فسيتم حظره في Lock.lock () للطريقة الداخلية ().
الفئة العامة reentrant2 {lock lock = new lock () ؛ Public Outer () {lock.lock () ؛ داخلي()؛ lock.unlock () ؛ } innerized inner () {lock.lock () ؛ // افعل شيئًا lock.unlock () ؛ }}سيؤدي ترابط الاتصال OUTER () أولاً إلى قفل مثيل القفل ثم يستمر في الاتصال بالاتصال الداخلي (). في الطريقة الداخلية () ، سيحاول مؤشر الترابط قفل مثيل القفل مرة أخرى ، وسوف يفشل الإجراء (أي ، سيتم حظر الخيط) ، لأن مثيل القفل مغلق بالفعل في الطريقة الخارجية ().
إذا لم يتم استدعاء Unlock () بين Lock () مرتين ، فسيتم حظر المكالمة الثانية للقفل. بعد رؤية تنفيذ Lock () ، ستجد أن السبب واضح:
قفل الطبقة العامة {boolean islocked = false ؛ رميات void void المزامنة العامة () interruptedException {بينما (islocked) {wait () ؛ } islocked = true ؛ } ...}ما إذا كان يُسمح لخيط بالخروج من طريقة القفل () يتم تحديدها من خلال الظروف في حلقة ". شرط الحكم الحالي هو أن عملية القفل مسموح بها فقط عندما تكون Islocked خاطئة ، دون النظر في أي مؤشر ترابط يغلقها.
من أجل جعل فئة القفل هذه إعادة إدخالها ، نحتاج إلى إجراء القليل من التغيير:
قفل الطبقة العامة {boolean islocked = false ؛ الموضوع lockedby = null ؛ int lockedCount = 0 ؛ قفل void المتزامن العام () يلقي interruptedException {thread callTherThread = thread.currentThread () ؛ بينما (isClocked && lockedby! = callingThread) {wait () ؛ } islocked = true ؛ LockedCount ++ ؛ lockedby = callingThread ؛ } unlock void synchronized synchronized () {if (thread.currentThRead () == this.lockedby) {lockedCount-- ؛ if (lockedCount == 0) {Islocked = false ؛ إخطار () ؛ }}} ...}لاحظ أن التيار أثناء حلقة (قفل الدوران) يأخذ أيضًا في الاعتبار الخيط الذي أغلق مثيل القفل. إذا لم يتم قفل كائن القفل الحالي (ISLOCHED = false) ، أو أن مؤشر ترابط الاتصال الحالي قد أغلق مثيل القفل ، فلن يتم تنفيذ حلقة بينما ، ويمكن أن يخرج قفل استدعاء مؤشر الترابط () من الطريقة (ملاحظة المترجم: "مسموح بالخروج من الطريقة" في الدلالات الحالية ، فإنها لن تتصل بالانتظار () وتسبب الحدود).
بالإضافة إلى ذلك ، نحتاج إلى تسجيل عدد المرات التي يقوم فيها نفس الخيط بشكل متكرر بإغلاق كائن قفل. خلاف ذلك ، فإن مكالمة unblock () ستعمل على إلغاء حظر القفل بأكمله ، حتى لو تم قفل القفل الحالي عدة مرات. لا نريد إلغاء قفل القفل حتى تصل مكالمة إلغاء القفل () إلى عدد المرات التي يتم فيها استدعاء استدعاء القفل () المقابل.
الآن هذا فئة القفل هو إعادة إدخال.
عدالة القفل
لا تضمن كتل Java المتزامنة الترتيب الذي تحاول به مؤشرات الترابط إدخالها. لذلك ، إذا استمرت مؤشرات ترابط متعددة في المنافسة للوصول إلى نفس كتلة المزامنة المتزامنة ، فهناك خطر من أن واحدة أو أكثر من مؤشرات الترابط لن تحصل على الوصول أبدًا - أي ، يتم تخصيص الوصول دائمًا إلى مؤشرات ترابط أخرى. هذا الموقف يسمى الجوع الخيط. لتجنب هذه المشكلة ، تحتاج الأقفال إلى تحقيق الإنصاف. يتم تنفيذ الأقفال الموضحة في هذه المقالة داخليًا باستخدام كتل المزامنة المتزامنة ، لذلك لا تضمن أن تكون عادلة.
استدعاء فتح () في بيان أخيرًا
إذا تم استخدام القفل لحماية المنطقة الحرجة ، وقد يلقي المنطقة الحرجة استثناء ، فمن المهم للغاية استدعاء Unlock () في البيان أخيرًا. هذا يضمن أنه يمكن إلغاء قفل كائن القفل بحيث يمكن أن تستمر مؤشرات الترابط الأخرى في قفلها. هنا مثال:
lock.lock () ؛ حاول {// do critic section code ، // التي قد ترمي استثناء} أخيرًا {lock.unlock () ؛}يضمن هذا الهيكل البسيط إلغاء قفل كائن القفل عند إلقاء استثناء في المنطقة الحرجة. إذا لم يتم استدعاء Unlock () في بيان أخيرًا ، عندما يتم إلقاء استثناء في القسم الحرج ، سيبقى كائن القفل في الحالة المقفلة إلى الأبد ، مما سيؤدي إلى حظر جميع مؤشرات الترابط الأخرى على كائن القفل.
ما ورد أعلاه هو المعلومات حول قفل Java متعدد الخيوط. سنستمر في إضافة المعلومات ذات الصلة في المستقبل. شكرا لك على دعمك لهذا الموقع!