登录
/
注册
首页
论坛
其它
首页
科技
业界
安全
程序
广播
Follow
关于
博客
发1篇日志+1圆
记录
发1条记录+2圆币
发帖说明
VIP申请
登录
/
注册
账号
自动登录
找回密码
密码
登录
立即注册
搜索
搜索
关闭
CSDN热搜
程序园
精品问答
技术交流
资源下载
本版
帖子
用户
软件
问答
教程
代码
VIP申请
VIP网盘
网盘
联系我们
道具
勋章
任务
设置
我的收藏
退出
腾讯QQ
微信登录
返回列表
首页
›
业界区
›
业界
›
【深入理解ReentrantReadWriteLock】读写分离与锁降级实 ...
【深入理解ReentrantReadWriteLock】读写分离与锁降级实践
[ 复制链接 ]
洪思思
2025-7-14 14:41:21
一、读写锁的核心价值
在多线程编程中,同步机制是保证线程安全的关键。传统的互斥锁(如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 + "
深入
理解
读写
分离
相关帖子
二次函数的深层理解、题目技巧和应用
kafka 副本集设置和理解
MySQL 28 读写分离有哪些坑?
理解 SOLID 原则:编写更简洁的 JavaScript 代码
深入解析权重轮询算法:非平滑与平滑实现的原理与数学依据
从JSON到Protobuf,深入序列化方案的选型与原理
6. LangChain4j + 多模态视觉理解详细说明
D哥asp脚本扫目录读写信息wt
GetACL可读写目录探测
vip免费申请,1年只需15美金$
回复
使用道具
举报
提升卡
置顶卡
沉默卡
喧嚣卡
变色卡
千斤顶
照妖镜
相关推荐
安全
二次函数的深层理解、题目技巧和应用
0
865
轩辕娅童
2025-08-23
业界
kafka 副本集设置和理解
0
216
玛凶
2025-08-23
业界
MySQL 28 读写分离有哪些坑?
0
60
许娴广
2025-08-24
业界
理解 SOLID 原则:编写更简洁的 JavaScript 代码
0
265
益竹月
2025-08-27
业界
深入解析权重轮询算法:非平滑与平滑实现的原理与数学依据
0
1013
匡菲
2025-08-28
业界
从JSON到Protobuf,深入序列化方案的选型与原理
0
657
左优扬
2025-09-04
科技
6. LangChain4j + 多模态视觉理解详细说明
0
568
阙忆然
2025-09-04
程序
D哥asp脚本扫目录读写信息wt
0
20
新程序
2025-09-05
程序
GetACL可读写目录探测
0
7
新程序
2025-09-07
高级模式
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
登录
|
立即注册
回复
本版积分规则
回帖并转播
回帖后跳转到最后一页
浏览过的版块
代码
安全
程序
科技
签约作者
程序园优秀签约作者
发帖
洪思思
2025-7-14 14:41:21
关注
0
粉丝关注
21
主题发布
板块介绍填写区域,请于后台编辑
财富榜{圆}
敖可
9984
黎瑞芝
9990
杭环
9988
4
凶契帽
9988
5
氛疵
9988
6
猷咎
9986
7
里豳朝
9986
8
肿圬后
9986
9
蝓俟佐
9984
10
虽裘侪
9984
查看更多