0Pricing
Java Academy · درس

ReentrantLock مقابل synchronized

قارن ميزات ReentrantLock، مثل tryLock وlockInterruptibly والإنصاف، بالكلمة المفتاحية synchronized

ReentrantLock مقابل synchronized درس مجاني في Java Academy على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Java Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Java Academy 4 دروس في المجموع.

الكلمة المفتاحية synchronized

توفر الكلمة المفتاحية synchronized في Java الاستبعاد المتبادل باستخدام القفل الداخلي للكائن (المراقب). وهي بسيطة، لكنها محدودة — فلا توفر مهلة أو إنصافًا أو قابلية للمقاطعة.

class Counter {
    private int count = 0;
    
    synchronized void increment() {
        count++; // only one thread at a time
    }
    
    synchronized int get() { return count; }
}

Counter c = new Counter();
// Multiple threads can safely call increment()

أساسيات ReentrantLock

‏ReentrantLock قفل صريح من java.util.concurrent.locks. ويوفر الاستبعاد المتبادل نفسه الذي يوفره synchronized، لكن مع تحكم أكبر. يجب تحرير القفل يدويًا داخل كتلة finally.

import java.util.concurrent.locks.*;

class SafeCounter {
    private int count = 0;
    private final ReentrantLock lock = new ReentrantLock();
    
    void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock(); // always release in finally!
        }
    }
    
    int get() {
        lock.lock();
        try { return count; }
        finally { lock.unlock(); }
    }
}

tryLock: الحصول على القفل دون حجب

تحاول tryLock() الحصول على القفل دون حجب التنفيذ. وتُرجع true إذا نجحت، وfalse إذا كان القفل محجوزًا بواسطة خيط آخر.

ReentrantLock lock = new ReentrantLock();

if (lock.tryLock()) {
    try {
        System.out.println("Got the lock, doing work");
    } finally {
        lock.unlock();
    }
} else {
    System.out.println("Lock busy, skipping or retrying");
}

tryLock مع مهلة

انتظر مدة محددة حتى الحصول على القفل:

try {
    if (lock.tryLock(500, TimeUnit.MILLISECONDS)) {
        try {
            // work with protected resource
            System.out.println("Acquired within 500ms");
        } finally {
            lock.unlock();
        }
    } else {
        System.out.println("Timed out waiting for lock");
    }
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
}

lockInterruptibly

تحصل lockInterruptibly() على القفل ما لم تتم مقاطعة الخيط — وهذا مفيد للمهام القابلة للإلغاء التي تنتظر قفلًا:

try {
    lock.lockInterruptibly(); // throws InterruptedException if interrupted
    try {
        // do work
    } finally {
        lock.unlock();
    }
} catch (InterruptedException e) {
    System.out.println("Interrupted while waiting for lock");
    Thread.currentThread().interrupt();
}

إعادة الدخول

كل من synchronized وReentrantLock قابل لإعادة الدخول — إذ يمكن لخيط يحمل القفل بالفعل الحصول عليه مرة أخرى دون حدوث توقف تام:

synchronized void outer() {
    System.out.println("outer");
    inner(); // same thread re-enters — OK
}

synchronized void inner() {
    System.out.println("inner"); // same lock, same thread
}

// ReentrantLock:
ReentrantLock lock = new ReentrantLock();
lock.lock();
lock.lock(); // acquire again — hold count = 2
lock.unlock(); // hold count = 1
lock.unlock(); // hold count = 0, lock released

سياسة الإنصاف

أنشئ ReentrantLock منصفًا لمنح الوصول بترتيب FIFO — فهذا يمنع حرمان الخيوط من التنفيذ، لكنه يأتي على حساب انخفاض معدل الإنتاجية:

// Unfair (default): no ordering guarantee, better throughput
ReentrantLock unfair = new ReentrantLock();

// Fair: threads acquire in arrival order
ReentrantLock fair = new ReentrantLock(true);

System.out.println(fair.isFair()); // true

متغيرات الشرط

يوفر ReentrantLock كائنات Condition — وهي أكثر مرونة من wait()/notify():

ReentrantLock lock2 = new ReentrantLock();
Condition notEmpty = lock2.newCondition();
Queue<String> queue = new LinkedList<>();

// Producer
lock2.lock();
try {
    queue.offer("item");
    notEmpty.signal(); // wake one waiting thread
} finally { lock2.unlock(); }

// Consumer
lock2.lock();
try {
    while (queue.isEmpty()) notEmpty.await(); // wait & release lock
    System.out.println(queue.poll());
} finally { lock2.unlock(); }

متى تستخدم ReentrantLock بدلًا من synchronized

استخدم synchronized عندما:

  • تحتاج إلى استبعاد متبادل بسيط
  • لا تحتاج إلى مهلة أو إنصاف أو قابلية للمقاطعة

استخدم ReentrantLock عندما:

  • تحتاج إلى tryLock() أو tryLock(timeout)
  • تحتاج إلى lockInterruptibly()
  • تحتاج إلى ترتيب منصف
  • تحتاج إلى عدة متغيرات Condition

عدد مرات الحصول على القفل ومراقبته

يوفر ReentrantLock أساليب تشخيصية:

ReentrantLock lock3 = new ReentrantLock();
lock3.lock();
lock3.lock(); // reentrant — hold count 2

System.out.println(lock3.getHoldCount());    // 2
System.out.println(lock3.isHeldByCurrentThread()); // true
System.out.println(lock3.isLocked());        // true
System.out.println(lock3.getQueueLength());  // 0 (no waiting threads)

lock3.unlock();
lock3.unlock(); // fully released

خطأ شائع: إلغاء القفل دون الحصول عليه

يؤدي استدعاء unlock() عندما لا يكون الخيط الحالي حاملًا للقفل إلى طرح IllegalMonitorStateException. احرص دائمًا على إقران lock مع unlock، أو استخدم try-finally:

ReentrantLock lock4 = new ReentrantLock();
try {
    // If lock() is never called (e.g., skipped by exception before this line)
    lock4.unlock(); // throws IllegalMonitorStateException
} catch (IllegalMonitorStateException e) {
    System.out.println("Must lock before unlock!");
}

اختبار سريع

ما أسلوب ReentrantLock الذي يسمح للخيط بالحصول على القفل أو بالعودة فورًا دون حجب التنفيذ إذا كان القفل غير متاح؟

مراجعة: ReentrantLock مقابل synchronized

أهم النقاط:

  • يوفر كلاهما الاستبعاد المتبادل وإعادة الدخول
  • يضيف ReentrantLock ما يلي: tryLock وlockInterruptibly والإنصاف وConditions
  • حرّر القفل دائمًا داخل كتلة finally
  • يُعد synchronized أبسط — ففضّله عندما تكون ميزاته كافية
  • استخدم ReentrantLock في الحالات المتقدمة: المهلات وقابلية المقاطعة والمتغيرات الشرطية المتعددة

الأسئلة الشائعة

هل درس «ReentrantLock مقابل synchronized» مجاني؟

نعم — نص درس «ReentrantLock مقابل synchronized» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Java Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Java Academy 4 دروس في المجموع.

ماذا ستتعلم في «ReentrantLock مقابل synchronized»؟

قارن ميزات ReentrantLock، مثل tryLock وlockInterruptibly والإنصاف، بالكلمة المفتاحية synchronized تتمرن على Java Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Java Academy؟

لا تُشترط خبرة سابقة. Java Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.

كم من الوقت يستغرق درس «ReentrantLock مقابل synchronized»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Java Academy هذا؟

نعم. كل درس في Java Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. ReentrantLock مقابل synchronized
  2. ReadWriteLock لسيناريوهات القارئ والكاتب
  3. المتغيرات الذرية: تحديثات دون أقفال
  4. StampedLock والقراءات المتفائلة
← العودة إلى Java Academy