قبل Java 5.0، لم يكن هناك سوى أقفال متزامنة ومتقلبة. بعد Java 5.0، تم تقديم قفل العرض ReentrantLock.
نظرة عامة على قفل إعادة الدخول
ReentrantLock هو قفل إعادة الدخول، وهو يختلف عن القفل المدمج، فهو يتطلب قفلًا وفتحًا صريحًا في كل مرة يتم استخدامه، ويوفر ميزات أكثر تقدمًا: القفل العادل، والقفل الموقوت، والقفل المشروط، والقفل القابل للاختراق يمكن أن يتجنب القفل بشكل فعال مشكلة الجمود التي تنفذها ReentrantLock
واجهة القفل:
انسخ رمز الكود كما يلي:
قفل الواجهة العامة {
// احظر حتى يتم الحصول على القفل أو مقاطعته
قفل باطل () ؛
// احظر حتى يتم الحصول على القفل أو يتم طرح استثناء عند المقاطعة
void lockInterruptible() يلقي InterruptedException؛
// احصل عليه فقط عندما يكون القفل متاحًا، وإلا فارجع مباشرة
محاولة منطقية () ؛
// احصل على القفل فقط إذا كان متاحًا خلال الوقت المحدد، وإلا فارجع مباشرة ورمي استثناءً عند المقاطعة
محاولة منطقية لـ (فترة طويلة، وحدة TimeUnit) تطرح InterruptedException؛
فتح باطلة ()؛
// إرجاع شرط مرتبط بهذا القفل
الحالة newCondition();
}
استخدام القفل
انسخ رمز الكود كما يلي:
قفل القفل = ReentrantLock () الجديد؛
lock.lock();
يحاول{
// تحديث حالة الكائن
}أخيراً{
// لاحظ هنا أنه يجب أن يكون هناك أخيرًا كتلة تعليمات برمجية لفتحها
// وإلا فمن السهل التسبب في حالة توقف تام ومشاكل أخرى في النشاط.
lock.unlock();
}
ميزات قفل إعادة الدخول
أقفال الاقتراع والأقفال الموقوتة
يتم تنفيذ طلبات القفل القابلة للتغيير والقابلة للتوقيت من خلال طريقة TryLock ()، والتي تختلف عن الحصول على الأقفال غير المشروطة. يمكن أن يكون لدى ReentrantLock آلية مرنة للتسامح مع الأخطاء يتم حظره عند القفل ولا يحرر القفل الذي يحمله بالفعل، مما يتسبب في النهاية في حالة توقف تام. عندما تحاول طريقة TryLock () الحصول على القفل، إذا كان القفل محتفظًا به بالفعل بواسطة مؤشر ترابط آخر، فسوف يعود فورًا وفقًا لطريقة الإعداد بدلاً من الحظر والانتظار، وفي الوقت نفسه، سيحرر القفل الذي يحتفظ به بعد ذلك يمكنك إعادته وفقًا لما تم إرجاعه، ونتيجة لذلك، يتم إجراء إعادة المحاولة أو الإلغاء لتجنب حالة الجمود.
الإنصاف
يوفر مُنشئ ReentrantLock خيارين: القفل العادل والقفل غير العادل (افتراضي). ما يسمى بالأقفال العادلة، ستحصل الخيوط على الأقفال بالترتيب الذي تصدر به الطلبات، ولا يُسمح بالقفز في قائمة الانتظار؛ ولكن بالنسبة للأقفال غير العادلة، يُسمح بالقفز في قائمة الانتظار: عندما يطلب مؤشر ترابط الحصول على قفل، إذا كان القفل متاحًا ، ثم سيتخطى هذا الخيط الخيط المنتظر في قائمة الانتظار ويحصل على القفل. نريد عمومًا أن تكون جميع الأقفال غير عادلة. لأنه عند إجراء عمليات القفل، فإن الإنصاف سيقلل الأداء بشكل كبير بسبب الحمل الزائد لتعليق الخيط واسترداد الخيط. خذ بعين الاعتبار الموقف: يحمل الخيط A قفلًا، ويطلب الخيط B القفل، لذلك يتم تعليق الخيط B؛ عندما يقوم الخيط A بتحرير القفل، سيتم إيقاظ الخيط B، لذلك يحاول الحصول على القفل مرة أخرى؛ يطلب C أيضًا الحصول على هذا القفل، فمن المرجح أن يحصل الخيط C على هذا القفل ويستخدمه ويحرره قبل إيقاظ الخيط B بالكامل. هذا وضع مربح للجانبين. اللحظة التي يحصل فيها B على القفل (لا يمكن لـ B الحصول على القفل إلا بعد استيقاظه) لا تتأخر في حصول C على القفل، كما يتم تحسين الإنتاجية. في معظم الحالات، يكون أداء الأقفال غير العادلة أعلى من أداء الأقفال العادلة.
يمكن مقاطعة عمليات الحصول على القفل
يكتسب الأسلوب lockInterruptible القفل بينما يظل مستجيبًا للمقاطعات، لذلك ليست هناك حاجة لإنشاء أنواع أخرى من عمليات الحظر غير المنقطعة.
قراءةWriteLockReadWriteLock
ReentrantLock هو قفل كائن مزامنة قياسي (يستطيع مؤشر ترابط واحد فقط الاحتفاظ بالقفل في المرة الواحدة). يختلف قفل القراءة والكتابة، فهو يعرض كائنين قفل، أحدهما يستخدم لعمليات القراءة والآخر لعمليات الكتابة.
انسخ رمز الكود كما يلي:
الواجهة العامة ReadWriteLock {
/**
* إرجاع القفل المستخدم للقراءة.
*
* @return القفل المستخدم للقراءة.
*/
قفل readLock();
/**
* إرجاع القفل المستخدم للكتابة.
*
* @return القفل المستخدم للكتابة.
*/
قفل writeLock();
}
التنفيذ الاختياري:
1. تحرير الأولوية
2. قراءة الموضوع يقفز في السطر
3. إعادة الدخول
4.الرجوع إلى إصدار سابق
5. الترقية
يطبق ReentrantReadWriteLock واجهة ReadWriteLock، ويوفر المنشئ طريقتين للإنشاء: القفل العادل والقفل غير العادل. تعتبر أقفال القراءة والكتابة مناسبة للمواقف التي تكون فيها القراءة أكثر والكتابة أقل، ويمكن أن تحقق توافقًا أفضل.
رمز النسخ النموذجي هو كما يلي:
فئة عامة ReadWriteMap<K, V> {
خريطة خاصة<K, V>;
قفل ReadWriteLock النهائي الخاص = new ReentrantReadWriteLock();
قفل نهائي خاص readLock = lock.readLock();
القفل النهائي الخاص writeLock = lock.writeLock();
ReadWriteMap العامة (خريطة<K, V>) {
this.map=map;
}
الحصول على V العام (مفتاح K) {
readLock.lock();
يحاول {
إرجاع Map.get(key);
} أخيراً {
readLock.unlock();
}
}
وضع الفراغ العام (مفتاح K، قيمة V) {
writeLock.lock();
يحاول {
Map.put(key, value);
} أخيراً {
writeLock.unlock();
}
}
}