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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- ReentrantLock กับ synchronized
- ReadWriteLock สำหรับสถานการณ์ผู้อ่าน-ผู้เขียน
- ตัวแปร Atomic: การอัปเดตแบบไร้ล็อก
- StampedLock และการอ่านเชิงคาดคะเน