ReadWriteLock لسيناريوهات القارئ والكاتب
استخدم ReadWriteLock للسماح بالقراءات المتزامنة مع ضمان الكتابات الحصرية في ذاكرة التخزين المؤقت
ReadWriteLock لسيناريوهات القارئ والكاتب درس مجاني في Java Academy على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Java Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Java Academy 4 دروس في المجموع.
مشكلة القارئ والكاتب
يمكن لعدة خيوط قراءة البيانات المشتركة بأمان في الوقت نفسه. أما الكتابة فتتطلب وصولًا حصريًا — فلا يُسمح بقراءات أو كتابات متزامنة. ويمثل ReadWriteLock هذا النموذج: عدة قراء متزامنين أو كاتب حصري واحد.
import java.util.concurrent.locks.*;
ReadWriteLock rwLock = new ReentrantReadWriteLock();
Lock readLock = rwLock.readLock();
Lock writeLock = rwLock.writeLock();قفل القراءة: وصول مشترك
يمكن لعدة خيوط حمل قفل القراءة في الوقت نفسه، ما دام لا يوجد خيط يحمل قفل الكتابة:
class ReadableCache {
private final Map<String, String> cache = new HashMap<>();
private final ReadWriteLock lock = new ReentrantReadWriteLock();
String get(String key) {
lock.readLock().lock();
try {
return cache.get(key); // concurrent reads OK
} finally {
lock.readLock().unlock();
}
}
}قفل الكتابة: وصول حصري
يمكن لخيط واحد فقط حمل قفل الكتابة في كل مرة. ويُحجب جميع القراء والكتّاب الآخرين أثناء الكتابة:
class WritableCache extends ReadableCache {
private final ReadWriteLock wLock = new ReentrantReadWriteLock();
private final Map<String, String> data = new HashMap<>();
void put(String key, String value) {
wLock.writeLock().lock();
try {
data.put(key, value); // exclusive write
} finally {
wLock.writeLock().unlock();
}
}
}مثال كامل لذاكرة التخزين المؤقت
ذاكرة تخزين مؤقت آمنة للخيوط، تهيمن فيها عمليات القراءة وتكون عمليات الكتابة نادرة:
class Cache<K,V> {
private final Map<K,V> map = new HashMap<>();
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
V get(K key) {
lock.readLock().lock();
try { return map.get(key); }
finally { lock.readLock().unlock(); }
}
void put(K key, V value) {
lock.writeLock().lock();
try { map.put(key, value); }
finally { lock.writeLock().unlock(); }
}
int size() {
lock.readLock().lock();
try { return map.size(); }
finally { lock.readLock().unlock(); }
}
}خفض مستوى القفل
يدعم ReadWriteLock خفض مستوى القفل: الحصول على قفل الكتابة ← الحصول على قفل القراءة ← تحرير قفل الكتابة. يتيح ذلك للخيط الانتقال من كتابة حصرية إلى قراءة مشتركة دون تحرير القفل:
ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
Lock r = rwl.readLock(), w = rwl.writeLock();
w.lock(); // acquire write
try {
// modify data
r.lock(); // acquire read WHILE holding write
} finally {
w.unlock(); // release write, now holding read only
}
try {
// safely read the just-written data
} finally {
r.unlock();
}لا يدعم قفل القراءة الترقية
ترقية القفل (من القراءة إلى الكتابة) غير مدعومة. فإذا حاول خيط يحمل قفل القراءة الحصول على قفل الكتابة، فسيحدث توقف تام لأن قفل الكتابة ينتظر جميع القراء، بمن فيهم الخيط نفسه:
// DEADLOCK: Do NOT do this
lock.readLock().lock();
try {
lock.writeLock().lock(); // DEADLOCK — waits for readLock to release
} finally {
lock.readLock().unlock();
}متى يكون ReadWriteLock مفيدًا
يكون ReadWriteLock مفيدًا عندما:
- يفوق عدد القراءات عدد الكتابات بفارق كبير
- تستغرق عمليات القراءة وقتًا ملحوظًا (مثل الاستعلامات المعقدة)
وقد يضعف الأداء عندما تكون الكتابات متكررة (فقفل الكتابة يحجب جميع القراء)، أو عندما تكون العمليات سريعة جدًا (بسبب تكلفة كائني القفل).
مقارنة ReadWriteLock مع synchronized
synchronized/ReentrantLock → خيط واحد فقط في كل مرة (حتى القراءات المتزامنة تكون محجوبة).
ReadWriteLock → عدة قراء متزامنين وكاتب حصري. يوفر معدل إنتاجية أفضل للقراءة في السيناريوهات التي تهيمن فيها القراءات.
// synchronized: only one thread reads at a time
synchronized String get(String key) { return cache.get(key); }
// ReadWriteLock: many threads can read simultaneously
String get2(String key) {
lock.readLock().lock();
try { return cache.get(key); }
finally { lock.readLock().unlock(); }
}اعتبارات الأداء
يؤدي الحصول على كائني قفل وتحريرهما إلى تكلفة إضافية مقارنة بقفل واحد. أجرِ تحليلًا للأداء قبل افتراض أن ReadWriteLock أسرع. تكون الفائدة واضحة في السيناريوهات التي تهيمن فيها القراءات وتستغرق العمليات وقتًا طويلًا. أما أعباء العمل السريعة التي تهيمن فيها الكتابات، فقد يكون ReentrantLock البسيط أسرع.
بديل ConcurrentHashMap
بالنسبة إلى عمليات الخرائط البسيطة، يكون ConcurrentHashMap غالبًا أسرع من HashMap + ReadWriteLock، لأنه يستخدم أقفالًا داخلية على مستوى المقاطع دون تنازع شامل:
// Often better than HashMap + ReadWriteLock for simple operations:
Map<String, String> concurrent = new ConcurrentHashMap<>();
concurrent.put("key", "value"); // thread-safe, no explicit lock
String v = concurrent.get("key"); // thread-safeإنصاف قفل القراءة
يكون ReentrantReadWriteLock، افتراضيًا، غير منصف (وقد تُحرم الكتّاب من التنفيذ). استخدم المُنشئ المنصف لمنع حرمان الكتّاب من التنفيذ:
// Fair: waiting writers are served before new readers
ReentrantReadWriteLock fairLock = new ReentrantReadWriteLock(true);
System.out.println(fairLock.isFair()); // trueاختبار سريع
في تطبيق تهيمن فيه القراءات، ما الميزة الأساسية لـ ReadWriteLock مقارنةً بأسلوب synchronized بسيط؟
مراجعة: ReadWriteLock
أهم النقاط:
- قراءات متزامنة متعددة أو كتابة حصرية واحدة — وليس كلاهما
- استخدم readLock.lock()/unlock() للقراءات المشتركة
- استخدم writeLock.lock()/unlock() للكتابات الحصرية
- خفض مستوى القفل مدعوم؛ أما الترقية (من القراءة إلى الكتابة) فتسبب توقفًا تامًا
- الأفضل للعمليات غير البسيطة التي تهيمن فيها القراءات؛ واستخدم ConcurrentHashMap للخرائط البسيطة
الأسئلة الشائعة
هل درس «ReadWriteLock لسيناريوهات القارئ والكاتب» مجاني؟
نعم — نص درس «ReadWriteLock لسيناريوهات القارئ والكاتب» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Java Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Java Academy 4 دروس في المجموع.
ماذا ستتعلم في «ReadWriteLock لسيناريوهات القارئ والكاتب»؟
استخدم ReadWriteLock للسماح بالقراءات المتزامنة مع ضمان الكتابات الحصرية في ذاكرة التخزين المؤقت تتمرن على Java Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Java Academy؟
لا تُشترط خبرة سابقة. Java Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.
كم من الوقت يستغرق درس «ReadWriteLock لسيناريوهات القارئ والكاتب»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Java Academy هذا؟
نعم. كل درس في Java Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- ReentrantLock مقابل synchronized
- ReadWriteLock لسيناريوهات القارئ والكاتب
- المتغيرات الذرية: تحديثات دون أقفال
- StampedLock والقراءات المتفائلة