ReadWriteLock接口及其实现ReentrantReadWriteLock方法
发布时间 - 2026-01-11 01:56:38 点击率:次Java并发包的locks包里的锁基本上已经介绍得差不多了,ReentrantLock重入锁是个关键,在清楚的了解了同步器AQS的运行机制后,实际上再分析这些锁就会显得容易得多,这章节主讲另外一个重要的锁——ReentrantReadWriteLock读写锁。

ReentrantLock是一个独占锁,也就是说只能由一个线程获取锁,但如果场景是线程只做读的操作呢?这样ReentrantLock就不是很合适,读的线程并不需要保证其线程的安全性,任何一个线程都能去获取锁,只有这样才能尽可能地保证性能和效率。ReentrantReadWriteLock就是这样的一个锁,在其内部分为读锁和写锁,可以有N个读操作线程获取到写锁,但是只能有1个写操作线程获取到写锁,那么可以预见的是写锁是共享锁(AQS中的共享模式),读锁是独占锁(AQS中的独占模式)。首先来看读写锁的接口类:
public interface ReadWriteLock {
Lock readLock(); //获取读锁
Lock writeLock(); //获取写锁
}
可以看到ReadWriteLock接口只定义了两个方法,获取读锁和获取写锁的方法。下面是ReadWriteLock的实现类——ReentrantReadWriteLock。
和ReentrantLock类似,ReentrantReadWriteLock在其内部也是通过一个内部类Sync实现同步器AQS,同样也是通过实现Sync实现公平锁和非公平锁,这一点的思路和ReentrantLock类似。在ReadWriteLock接口中获取的读锁和写锁是怎么实现的呢?
//ReentrantReadWriteLock
private final ReentrantReadWriteLock.ReadLock readerLock;
private final ReentrantReadWriteLock.WriteLock writerLock;
final Sync sync;
public ReentrantReadWriteLock(){
this(false); //默认非公平锁
}
public ReentrantReadWriteLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync(); //锁类型(公平/非公平)
readerLock = new ReadLock(this); //构造读锁
writerLock = new WriteLock(this); //构造写锁
}
……
public ReentrantReadWriteLock.WriteLock writeLock0{return writerLock;}
public ReentrantReadWriteLock.ReadLock readLock0{return ReaderLock;}
//ReentrantReadWriteLock$ReadLock
public static class ReadLock implements Lock {
protected ReadLock(ReentrantReadwritLock lock) {
sync = lock.sync; //最后还是通过Sync内部类实现锁
}
…… //它实现的是Lock接口,其余的实现可以和ReentrantLock作对比,获取锁、释放锁等等
}
//ReentrantReadWriteLock$WriteLock
public static class WriteLock implemnts Lock {
protected WriteLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
…… //它实现的是Lock接口,其余的实现可以和ReentrantLock作对比,获取锁、释放锁等等
}
上面是对ReentrantReadWriteLock做了一个大致的介绍,可以看到在其内部有好几个内部类,实际上读写锁内有两个锁——ReadLock、WriteLock,这两个锁都是实现自Lock接口,可以和ReentrantLock对比,而这两个锁的内部实现则是通过Sync,也就是同步器AQS实现的,这也可以和ReentrantLock中的Sync对比。
回顾一下AQS,其内部有两个重要的数据结构——一个是同步队列、一个则是同步状态,这个同步状态应用到读写锁中也就是读写状态,但AQS中只有一个state整型来表示同步状态,读写锁中则有读、写两个同步状态需要记录。所以,读写锁将AQS中的state整型做了一下处理,它是一个int型变量一共4个字节32位,那么可以读写状态就可以各占16位——高16位表示读,低16位表示写。
现在有一个疑问如果state的值位5,二进制为(00000000000000000000000000000101),如何快速确定读和写各自的状态呢?这就要用到位移运算了。计算方式为:写状态state & 0x0000FFFF,读状态state >>> 16。写状态增加1等于state + 1,读状态增加1等于state + (1 << 16)。有关移位运算可以参考《<<、>>、>>>移位操作》。
写锁的获取与释放
根据我们之前的经验可以得知:AQS已经将获取锁的算法骨架搭好了,只需子类实现tryAcquire(独占锁),故我们只需查看tryAcquire。
//ReentrantReadWriteLock$Sync
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread;
int c = getState(); //获取state状态
int w = exclusiveCount(c); //获取写状态,即 state & 0x00001111
if (c != 0) { //存在同步状态(读或写),作下一步判断
if (w == 0 || current != getExclusiveOwnerThread()) //写状态为0,但同步状态不为0表示有读状态,此时获取锁失败,或者当前已经有其他写线程获取了锁此时也获取锁失败
return false;
if (w + exclusiveCount(acquire) > MAX_COUNT) //锁重入是否超过限制
throw new Error(“Maxium lock count exceeded”);
setState(c + acquire); //记录锁状态
return true;
}
if (writerShouldBlock() || !compareAndSetState(c, c + acquires))
return false; //writerShouldBlock对于非公平锁总是返回false,对于公平锁则判断同步队列中是否有前驱节点
setExclusiveOwnerThread(current);
return true;
}
上面是写锁的状态获取,不好理解的是writerShouldBlock方法,此方法上面有描述,非公平锁直接返回false,而对于公平锁则是调用hasQueuedPredecessors方法如下:
//ReentrantReadWriteLock$FairSync
final boolean writerShouldBlock() {
return hasQueuedPredecessors();
}
原因是为什么呢?这就要回到非公平锁和公平锁的区别上来了,简单回顾一下,详情可参考《5.Lock接口及其实现ReentrantLock》。对于非公平锁,每次线程获取锁时首先会强行进行锁获取操作而不管同步队列中是否有线程,当获取不到时才会将线程构造至队尾;对于公平锁来讲,只要同步队列中存在线程,就不会去获取锁,而是将线程构造添加至队尾。所以重新回到写状态的获取上,tryAcquire方法里,前面发现没有线程持有锁,但是此时会根据锁的不同做相应操作,对于非公平锁——抢锁,对公平锁——同步队列中有线程,不抢锁,添加至队尾排队。
写锁的释放与ReentrantLock的释放过程基本类似,毕竟都是独占锁,每次释放减少写的状态,直到减小到0就表示写锁已经完全释放。
读锁的获取与释放
同理,根据我们之前的经验可以得知:AQS已经将获取锁的算法骨架搭好了,只需子类实现tryAcquireShared(共享锁),故我们只需查看tryAcquireShared。我们知道对于共享模式下的锁,它能够被多个线程同时获取,现在问题来了,T1线程获取了锁,同步状态state=1,此时T2也获取了锁,state=2,接着T1线程重入state=3,也就是说读状态是所有线程读锁次数的总和,而每个线程各自获取读锁的次数只能选择保存在ThreadLock中,由线程自身维护,所以在这个地方要做一些复杂处理,源码有点长,但复杂就在于每个线程保存自身获取读锁的次数,具体参照源码的tryAcquireShared,仔细阅读并结合上面对写锁获取的分析不难读懂。
读锁的释放值得注意的地方在于自身维护的获取锁的次数,以及通过移位操作减少状态state – (1 << 16)。
以上这篇ReadWriteLock接口及其实现ReentrantReadWriteLock方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。
# 实现ReentrantReadWriteLock
# Java concurrency之共享锁和ReentrantReadWriteLock_动力节点Ja
# Java多线程 ReentrantReadWriteLock原理及实例详解
# Java多线程之ReentrantReadWriteLock源码解析
# ReentrantReadWriteLock不能锁升级的原因总结
# Java多线程读写锁ReentrantReadWriteLock类详解
# ReentrantReadWriteLock 读写锁分析总结
# 的是
# 只需
# 则是
# 都是
# 子类
# 这就
# 给大家
# 可以看到
# 是一个
# 整型
# 也就是说
# 就会
# 是个
# 来了
# 搭好
# 在这个
# 多个
# 就不
# 都能
# 是怎么
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
Win11任务栏卡死怎么办 Windows11任务栏无反应解决方法【教程】
rsync同步时出现rsync: failed to set times on “xxxx”: Operation not permitted
Edge浏览器怎么启用睡眠标签页_节省电脑内存占用优化技巧
Python文件异常处理策略_健壮性说明【指导】
如何在Windows环境下新建FTP站点并设置权限?
如何快速搭建高效WAP手机网站?
高防服务器租用首荐平台,企业级优惠套餐快速部署
东莞专业网站制作公司有哪些,东莞招聘网站哪个好?
WEB开发之注册页面验证码倒计时代码的实现
香港服务器网站卡顿?如何解决网络延迟与负载问题?
Laravel PHP版本要求一览_Laravel各版本环境要求对照
HTML透明颜色代码怎么让下拉菜单透明_下拉菜单透明背景指南【技巧】
Laravel如何生成URL和重定向?(路由助手函数)
如何基于云服务器快速搭建网站及云盘系统?
网站制作企业,网站的banner和导航栏是指什么?
怎么用AI帮你为初创公司进行市场定位分析?
node.js报错:Cannot find module 'ejs'的解决办法
如何在云主机上快速搭建网站?
html5如何设置样式_HTML5样式设置方法与CSS应用技巧【教程】
昵图网官方站入口 昵图网素材图库官网入口
Laravel如何使用API Resources格式化JSON响应_Laravel数据资源封装与格式化输出
Laravel如何配置任务调度?(Cron Job示例)
专业型网站制作公司有哪些,我设计专业的,谁给推荐几个设计师兼职类的网站?
VIVO手机上del键无效OnKeyListener不响应的原因及解决方法
Linux系统命令中tree命令详解
iOS中将个别页面强制横屏其他页面竖屏
怎么制作一个起泡网,水泡粪全漏粪育肥舍冬季氨气超过25ppm,可以有哪些措施降低舍内氨气水平?
Laravel如何保护应用免受CSRF攻击?(原理和示例)
网站制作软件有哪些,制图软件有哪些?
C++用Dijkstra(迪杰斯特拉)算法求最短路径
手机钓鱼网站怎么制作视频,怎样拦截钓鱼网站。怎么办?
文字头像制作网站推荐软件,醒图能自动配文字吗?
如何在阿里云虚拟主机上快速搭建个人网站?
jimdo怎样用html5做选项卡_jimdo选项卡html5实现与切换效果【指南】
Laravel事件和监听器如何实现_Laravel Events & Listeners解耦应用的实战教程
高性能网站服务器配置指南:安全稳定与高效建站核心方案
zabbix利用python脚本发送报警邮件的方法
Android中Textview和图片同行显示(文字超出用省略号,图片自动靠右边)
如何在云主机快速搭建网站站点?
个人网站制作流程图片大全,个人网站如何注销?
Laravel如何构建RESTful API_Laravel标准化API接口开发指南
Laravel如何实现API速率限制?(Rate Limiting教程)
Android Socket接口实现即时通讯实例代码
Laravel如何实现图片防盗链功能_Laravel中间件验证Referer来源请求【方案】
JS碰撞运动实现方法详解
网站制作价目表怎么做,珍爱网婚介费用多少?
如何为不同团队 ID 动态生成多个“认领值班”按钮
公司门户网站制作流程,华为官网怎么做?
Laravel Docker环境搭建教程_Laravel Sail使用指南
如何快速上传自定义模板至建站之星?

