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