一、读写锁的核心价值
在多线程编程中,同步机制是保证线程安全的关键。传统的互斥锁(如synchronized)在读多写少的场景下存在明显性能瓶颈:读操作被不必要的串行化,即使多个线程只读取数据也会相互阻塞。这正是ReentrantReadWriteLock的用武之地!
读写锁的优势
- 读读并发:多个线程可以同时获取读锁
- 读写互斥:写锁独占时阻塞所有读写操作
- 写写互斥:同一时刻只允许一个写操作
- 锁降级:写锁可安全降级为读锁(本文重点)
二、ReentrantReadWriteLock实现原理
2.1 状态分离设计
ReentrantReadWriteLock通过AQS(AbstractQueuedSynchronizer)实现,其核心在于将32位state分为两部分:- // 状态位拆分示意
- static final int SHARED_SHIFT = 16; // 共享锁移位值
- static final int EXCLUSIVE_MASK = (1 << 16) - 1; // 独占锁掩码
- // 获取读锁数量(高16位)
- static int sharedCount(int c) {
- return c >>> SHARED_SHIFT;
- }
- // 获取写锁重入次数(低16位)
- static int exclusiveCount(int c) {
- return c & EXCLUSIVE_MASK;
- }
复制代码 2.2 锁获取规则
锁类型获取条件读锁无写锁持有,或持有写锁的是当前线程(锁降级情况)写锁无任何读锁且无其他线程持有写锁(可重入)2.3 工作流程对比
读锁获取流程:- 1. 检查是否有写锁持有
- ├─ 无:增加读锁计数,获取成功
- └─ 有:检查是否当前线程持有
- ├─ 是:获取成功(锁降级情况)
- └─ 否:进入等待队列
复制代码 写锁获取流程:- 1. 检查是否有任何锁
- ├─ 无:设置写锁状态,获取成功
- └─ 有:检查是否当前线程重入
- ├─ 是:增加写锁计数
- └─ 否:进入等待队列
复制代码 三、锁降级:原理与必要性
3.1 什么是锁降级?
锁降级(Lock Downgrading) 是指线程在持有写锁的情况下:
- // 标准锁降级流程
- writeLock.lock(); // 1.获取写锁
- try {
- // 修改数据...
- readLock.lock(); // 2.获取读锁(关键步骤)
- } finally {
- writeLock.unlock(); // 3.释放写锁(完成降级)
- }
- try {
- // 读取数据(受读锁保护)
- } finally {
- readLock.unlock(); // 4.释放读锁
- }
复制代码 3.2 为什么需要锁降级?
考虑以下无锁降级的危险场景:- 时间线:
- 1. 线程A获取写锁
- 2. 线程A修改数据
- 3. 线程A释放写锁
- 4. [危险间隙开始]
- 5. 线程B获取写锁
- 6. 线程B修改数据
- 7. 线程B释放写锁
- 8. [危险间隙结束]
- 9. 线程A获取读锁
- 10. 线程A读取到线程B修改的数据(非预期!)
复制代码 锁降级通过在释放写锁前获取读锁,消除了这个危险间隙:- 时间线:
- 1. 线程A获取写锁
- 2. 线程A修改数据
- 3. 线程A获取读锁
- 4. 线程A释放写锁
- 5. [读锁保护中]
- 6. 线程B尝试获取写锁(阻塞)
- 7. 线程A安全读取数据
- 8. 线程A释放读锁
- 9. 线程B获取写锁
复制代码 3.3 锁降级的核心价值
- 数据一致性:确保线程看到自己修改的最新数据
- 写后读原子性:消除写锁释放到读锁获取之间的危险窗口
- 并发性优化:允许其他读线程并发访问最新数据
四、完整代码示例
4.1 基础读写锁使用
- import java.util.concurrent.locks.ReentrantReadWriteLock;
- public class ReadWriteLockDemo {
- private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
- private final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();
- private final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();
- private int sharedData = 0;
- // 写操作
- public void writeData(int value) {
- writeLock.lock();
- try {
- System.out.println(Thread.currentThread().getName() + " 开始写入: " + value);
- sharedData = value;
- Thread.sleep(100); // 模拟写耗时
- System.out.println(Thread.currentThread().getName() + " 写入完成");
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- } finally {
- writeLock.unlock();
- }
- }
- // 读操作
- public void readData() {
- readLock.lock();
- try {
- System.out.println(Thread.currentThread().getName() + " 开始读取");
- Thread.sleep(50); // 模拟读耗时
- System.out.println(Thread.currentThread().getName() + " 读取到: " + sharedData);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- } finally {
- readLock.unlock();
- }
- }
- public static void main(String[] args) {
- ReadWriteLockDemo demo = new ReadWriteLockDemo();
-
- // 创建读线程
- for (int i = 0; i < 5; i++) {
- new Thread(() -> {
- while (true) {
- demo.readData();
- sleep(200);
- }
- }, "Reader-" + i).start();
- }
-
- // 创建写线程
- for (int i = 0; i < 2; i++) {
- int id = i;
- new Thread(() -> {
- int value = 0;
- while (true) {
- demo.writeData(value++);
- sleep(300);
- }
- }, "Writer-" + id).start();
- }
- }
-
- private static void sleep(long millis) {
- try {
- Thread.sleep(millis);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- }
- }
复制代码 执行效果说明:- Reader-0 开始读取
- Reader-1 开始读取 // 多个读线程可以并发
- Reader-0 读取到: 0
- Reader-1 读取到: 0
- Writer-0 开始写入: 0 // 写操作独占
- Writer-0 写入完成
- Reader-2 开始读取
- Reader-3 开始读取 // 写完成后读操作恢复并发
- Reader-2 读取到: 0
- Reader-3 读取到: 0
复制代码 4.2 锁降级实战
[code]import java.util.concurrent.locks.ReentrantReadWriteLock;public class LockDowngradeDemo { private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); private final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock(); private final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock(); private volatile boolean dataValid = false; private int criticalData = 0; public void processWithDowngrade() { // 1. 获取写锁 writeLock.lock(); try { // 2. 准备数据(写操作) System.out.println("[" + Thread.currentThread().getName() + "] 获取写锁,准备数据..."); prepareData(); // 3. 获取读锁(开始降级) readLock.lock(); System.out.println("[" + Thread.currentThread().getName() + "] 获取读锁(准备降级)"); } finally { // 4. 释放写锁(保留读锁) writeLock.unlock(); System.out.println("[" + Thread.currentThread().getName() + "] 释放写锁(完成降级)"); } try { // 5. 使用数据(读操作) System.out.println("[" + Thread.currentThread().getName() + "] 在降级保护下使用数据"); useData(); } finally { // 6. 释放读锁 readLock.unlock(); System.out.println("[" + Thread.currentThread().getName() + "] 释放读锁"); } } private void prepareData() { // 模拟数据准备(写操作) criticalData = (int) (Math.random() * 1000); dataValid = true; sleep(500); // 模拟耗时操作 } private void useData() { if (!dataValid) { System.err.println("数据无效!"); return; } // 模拟数据使用(读操作) System.out.println(">>> 使用关键数据: " + criticalData + " |