JAVA锁机制 & 线程ReentrantReadWriteLock 笔记及读写者问题实现
Liuxz一、ReentrantReadWriteLock 核心概念
ReentrantReadWriteLock 是 Java 并发包中用于解决读写者问题的同步工具,基于「读写分离锁」设计,包含两把锁:
- 读锁(SharedLock):允许多个线程同时获取,适合读操作(共享资源)。
- 写锁(ExclusiveLock):仅允许一个线程获取,适合写操作(独占资源)。
其核心特性是读写互斥、写写互斥、读读共享,完美满足读写者问题的 3 个基本要求。
二、锁的获取条件
获取读锁的前提:
- 没有线程持有写锁;
- 若有写请求,仅当当前线程是持有写锁的线程时(可重入)才能获取读锁。
获取写锁的前提:
- 没有线程持有读锁;
- 没有线程持有写锁;
- 当前线程可重入(已持有写锁时可再次获取)。
三、公平性策略
ReentrantReadWriteLock 支持两种模式(通过构造函数指定):
- 非公平模式(默认):读操作可能优先于写操作(读者可能持续抢占,导致写者饥饿)。
- 公平模式:按线程请求顺序获取锁(实现「弱写者优先 / 公平竞争」,避免饥饿)。
四、核心方法
| 方法 |
说明 |
readLock() |
获取读锁(Lock 接口实例) |
writeLock() |
获取写锁(Lock 接口实例) |
getReadLockCount() |
当前持有读锁的线程数(重入会累加) |
isWriteLocked() |
判断写锁是否被持有 |
五、公平竞争模式下的读写者问题实现
以下代码通过 ReentrantReadWriteLock 的公平模式,实现「弱写者优先(公平竞争)」的读写者问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.Lock;
public class ReadWriteDemo { private int data = 0; private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(true); private final Lock readLock = rwLock.readLock(); private final Lock writeLock = rwLock.writeLock();
public int read() { readLock.lock(); try { System.out.println(Thread.currentThread().getName() + " 开始读数据,当前数据:" + data); Thread.sleep(500); return data; } catch (InterruptedException e) { Thread.currentThread().interrupt(); return -1; } finally { System.out.println(Thread.currentThread().getName() + " 读完数据"); readLock.unlock(); } }
public void write(int newData) { writeLock.lock(); try { System.out.println(Thread.currentThread().getName() + " 开始写数据,新数据:" + newData); Thread.sleep(1000); data = newData; } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { System.out.println(Thread.currentThread().getName() + " 写完数据"); writeLock.unlock(); } }
public static void main(String[] args) { ReadWriteDemo demo = new ReadWriteDemo();
for (int i = 0; i < 3; i++) { new Thread(() -> { while (true) { demo.read(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }, "Reader-" + i).start(); }
for (int i = 0; i < 2; i++) { final int num = i; new Thread(() -> { int value = 0; while (true) { demo.write(value + num * 100); value++; try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } }, "Writer-" + i).start(); } } }
|
六、代码说明
- 共享资源:
data 变量模拟需要并发读写的数据。
- 锁初始化:
ReentrantReadWriteLock(true) 表示公平模式,线程按请求顺序获取锁。
- 读操作:通过
readLock 保证多个读者可同时读取,读取时阻塞写者。
- 写操作:通过
writeLock 保证独占写入,写入时阻塞所有读者和其他写者。
- 公平性体现:写者不会被读者持续抢占(非公平模式可能出现),符合「公平竞争」要求。
七、注意事项
- 重入性:读锁和写锁都支持重入(同一线程可多次获取),但读锁不能升级为写锁(避免死锁)。
- 性能:读操作频繁时,读写锁效率远高于
ReentrantLock(减少锁竞争);写操作频繁时,优势不明显。
- 饥饿问题:非公平模式可能导致写者饥饿,公平模式通过排队解决,但性能略低。
通过 ReentrantReadWriteLock,可简洁高效地解决读写者问题的各种场景(读者优先、公平竞争)。