0Pricing
Java Academy · บทเรียน

ReentrantLock กับ synchronized

เปรียบเทียบความสามารถของ ReentrantLock ได้แก่ tryLock, lockInterruptibly และความเป็นธรรม กับคีย์เวิร์ด synchronized

ReentrantLock กับ synchronized เป็นบทเรียน Java Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Java Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน

คีย์เวิร์ดการล็อกอัตโนมัติ

คีย์เวิร์ด 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 โดยให้การกีดกันการเข้าถึงเช่นเดียวกับการล็อกอัตโนมัติ แต่ควบคุมได้มากกว่า ต้องปล่อยล็อกด้วยตนเองภายในบล็อก 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() พยายามได้มาซึ่งล็อกโดยไม่บล็อกการทำงาน คืนค่าเป็นจริงหากสำเร็จ และคืนค่าเป็นเท็จหากเธรดอื่นถือล็อกอยู่

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();
}

การเข้าล็อกซ้ำ

ทั้งการล็อกอัตโนมัติและ 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 หรือการล็อกอัตโนมัติเมื่อใด

ใช้การล็อกอัตโนมัติเมื่อ:

  • ต้องการเพียงการกีดกันการเข้าถึงแบบง่าย
  • ไม่จำเป็นต้องมีการหมดเวลา นโยบายความเป็นธรรม หรือการรองรับการขัดจังหวะ

ใช้ ReentrantLock เมื่อ:

  • จำเป็นต้องใช้ tryLock() หรือ tryLock(timeout)
  • จำเป็นต้องใช้ lockInterruptibly()
  • ต้องการลำดับที่เป็นธรรม
  • ต้องใช้ตัวแปรเงื่อนไขหลายตัว

จำนวนครั้งที่ล็อกและการตรวจติดตาม

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 เทียบกับการล็อกอัตโนมัติ

ประเด็นสำคัญ:

  • ทั้งสองแบบให้การกีดกันการเข้าถึงและรองรับการเข้าล็อกซ้ำ
  • ReentrantLock เพิ่มความสามารถ ได้แก่ tryLock, lockInterruptibly, ความเป็นธรรม และเงื่อนไข
  • ปล่อยล็อกในบล็อก finally เสมอ
  • การล็อกอัตโนมัติเรียบง่ายกว่า — ควรเลือกใช้เมื่อความสามารถของมันเพียงพอ
  • ใช้ ReentrantLock สำหรับกรณีขั้นสูง เช่น การหมดเวลา การรองรับการขัดจังหวะ และเงื่อนไขหลายตัว

คำถามที่พบบ่อย

บทเรียน “ReentrantLock กับ synchronized” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “ReentrantLock กับ synchronized” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Java Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Java Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “ReentrantLock กับ synchronized”

เปรียบเทียบความสามารถของ ReentrantLock ได้แก่ tryLock, lockInterruptibly และความเป็นธรรม กับคีย์เวิร์ด synchronized คุณปฏิบัติ Java Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Java Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Java Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน

บทเรียน “ReentrantLock กับ synchronized” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Java Academy นี้ได้ไหม

ได้ บทเรียน Java Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. ReentrantLock กับ synchronized
  2. ReadWriteLock สำหรับสถานการณ์ผู้อ่าน-ผู้เขียน
  3. ตัวแปร Atomic: การอัปเดตแบบไร้ล็อก
  4. StampedLock และการอ่านเชิงคาดคะเน
← กลับไปที่ Java Academy