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

ReadWriteLock สำหรับสถานการณ์ผู้อ่าน-ผู้เขียน

ใช้ ReadWriteLock เพื่ออนุญาตให้หลายการอ่านเกิดขึ้นพร้อมกัน พร้อมรับรองว่าการเขียนเกิดขึ้นแบบเอกสิทธิ์ในแคช

ReadWriteLock สำหรับสถานการณ์ผู้อ่าน-ผู้เขียน เป็นบทเรียน Java Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 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();
}

ล็อกอ่านไม่สามารถยกระดับได้

ไม่รองรับการยกระดับล็อก (อ่าน → เขียน) หากเธรดที่ถือล็อกอ่านพยายามได้มาซึ่งล็อกเขียน จะเกิดการติดตาย เพราะล็อกเขียนจะรอผู้อ่าน ALL ตัว รวมถึงตัวมันเอง:

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

การล็อกอัตโนมัติ/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 เหนือเมธอดที่ใช้การล็อกอัตโนมัติแบบง่ายคืออะไร

สรุป: ReadWriteLock

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

  • อ่านพร้อมกันได้หลายตัว หรือเขียนแบบมีสิทธิ์แต่เพียงผู้เดียวได้หนึ่งตัว — ไม่สามารถทำทั้งสองอย่างพร้อมกัน
  • ใช้ readLock.lock()/unlock() สำหรับการอ่านร่วมกัน
  • ใช้ writeLock.lock()/unlock() สำหรับการเขียนแบบมีสิทธิ์แต่เพียงผู้เดียว
  • รองรับการลดระดับล็อก แต่การยกระดับ (อ่าน→เขียน) ทำให้เกิดการติดตาย
  • เหมาะที่สุดกับงานที่มีการอ่านเป็นส่วนใหญ่และการดำเนินการไม่น้อยเกินไป ส่วน ConcurrentHashMap เหมาะกับแผนที่แบบง่าย

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

บทเรียน “ReadWriteLock สำหรับสถานการณ์ผู้อ่าน-ผู้เขียน” ฟรีหรือไม่

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

คุณจะเรียนรู้อะไรในบทเรียน “ReadWriteLock สำหรับสถานการณ์ผู้อ่าน-ผู้เขียน”

ใช้ ReadWriteLock เพื่ออนุญาตให้หลายการอ่านเกิดขึ้นพร้อมกัน พร้อมรับรองว่าการเขียนเกิดขึ้นแบบเอกสิทธิ์ในแคช คุณปฏิบัติ Java Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

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

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

บทเรียน “ReadWriteLock สำหรับสถานการณ์ผู้อ่าน-ผู้เขียน” ใช้เวลานานแค่ไหน

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

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

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

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

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