深入ReentrantReadWriteLock(一)

一、为什么要出现读写锁

synchronized和ReentrantLock都是互斥锁。
如果说有一个操作是读多写少的,还要保证线程安全的话。如果采用上述的两种互斥锁,效率方面很定是很低的。
在这种情况下,咱们就可以使用ReentrantReadWriteLock读写锁去实现。
读读之间是不互斥的,可以读和读操作并发执行。
但是如果涉及到了写操作,那么还得是互斥的操作。

static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
static ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
static ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
readLock.lock();
try {
System.out.println("子线程!");
try {
Thread.sleep(500000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
readLock.unlock();
}
}).start();Thread.sleep(1000);
writeLock.lock();
try {
System.out.println("主线程!");
} finally {
writeLock.unlock();
}
}

二、读写锁的实现原理

ReentrantReadWriteLock还是基于AQS实现的,还是对state进行操作,拿到锁资源就去干活,如果没有拿到,依然去AQS队列中排队。
读锁操作:基于state的高16位进行操作。
写锁操作:基于state的低16为进行操作。
ReentrantReadWriteLock依然是可重入锁。
写锁重入:读写锁中的写锁的重入方式,基本和ReentrantLock一致,没有什么区别,依然是state进行+1操作即可,只要确认持有锁资源的线程,是当前写锁线程即可。只不过之前ReentrantLock的重入次数是state的正数取值范围,但是读写锁中写锁范围就变小了。
读锁重入:因为读锁是共享锁。读锁在获取锁资源操作时,是要对state的高16位进行 + 1操作。因为读锁是共享锁,所以同一时间会有多个读线程持有读锁资源。这样一来,多个读操作在持有读锁时,无法确认每个线程读锁重入的次数。为了去记录读锁重入的次数,每个读操作的线程,都会有一个ThreadLocal记录锁重入的次数。
写锁的饥饿问题:读锁是共享锁,当有线程持有读锁资源时,再来一个线程想要获取读锁,直接对state修改即可。在读锁资源先被占用后,来了一个写锁资源,此时,大量的需要获取读锁的线程来请求锁资源,如果可以绕过写锁,直接拿资源,会造成写锁长时间无法获取到写锁资源。
读锁在拿到锁资源后,如果再有读线程需要获取读锁资源,需要去AQS队列排队。如果队列的前面需要写锁资源的线程,那么后续读线程是无法拿到锁资源的。持有读锁的线程,只会让写锁线程之前的读线程拿到锁资源。

三、写锁分析

3.1 写锁加锁流程概述

3.2 写锁加锁源码分析

写锁加锁流程

// 写锁加锁的入口
public void lock() {
sync.acquire(1);
}
// 阿巴阿巴!!
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();
}
// 读写锁的写锁实现tryAcquire
protected final boolean tryAcquire(int acquires) {
// 拿到当前线程
Thread current = Thread.currentThread();
// 拿到state的值
int c = getState();
// 得到state低16位的值
int w = exclusiveCount(c);
// 判断是否有线程持有着锁资源
if (c != 0) {
// 当前没有线程持有写锁,读写互斥,告辞。
// 有线程持有写锁,持有写锁的线程不是当前线程,不是锁重入,告辞。
if (w == 0 || current != getExclusiveOwnerThread())
return false;
// 当前线程持有写锁。 锁重入。
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// 没有超过锁重入的次数,正常 + 1
setState(c + acquires);
return true;
}
// 尝试获取锁资源
if (writerShouldBlock() ||
// CAS拿锁!compareAndSetState(c, c + acquires))
return false;
// 拿锁成功,设置占有互斥锁的线程
setExclusiveOwnerThread(current);
// 返回true
return true;
}
// ================================================================
// 这个方法是将state的低16位的值拿到
int w = exclusiveCount(c);
state & ((1 << 16) - 1)
00000000 00000000 00000000 00000001 == 1
00000000 00000001 00000000 00000000 == 1 << 16
00000000 00000000 11111111 11111111 == (1 << 16) - 1
&运算,一个为0,必然为0,都为1,才为1
// ================================================================
// writerShouldBlock方法查看公平锁和非公平锁的效果
// 非公平锁直接返回false执行CAS尝试获取锁资源
// 公平锁需要查看是否有排队的,如果有排队的,我是否是head的next

3.3 写锁释放锁流程概述&释放锁源码

释放的流程和ReentrantLock一致,只是在判断释放是否干净时,判断低16位的值

// 写锁释放锁的tryRelease方法
protected final boolean tryRelease(int releases) {
// 判断当前持有写锁的线程是否是当前线程
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
// 获取state - 1
int nextc = getState() - releases;
// 判断低16位结果是否为0,如果为0,free设置为true
boolean free = exclusiveCount(nextc) == 0;
if (free)
// 将持有锁的线程设置为null
setExclusiveOwnerThread(null);
// 设置给state
setState(nextc);
// 释放干净,返回true。 写锁有冲入,这里需要返回false,不去释放排队的Node
return free;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/165452.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

React16中打印事件对象取不到值的现象及其原因分析

React16中打印事件对象取不到值的现象及其原因分析 一、背景 在最近的开发过程中&#xff0c;遇到了一个看起来匪夷所思的问题❓&#xff1a; <Inputplaceholder"请输入"onChange{(e) > {console.log(e:, e)}}onKeyDown{handleKeyDown} />此时按理来说我…

旅行商问题(枚举,回溯,动态规划,贪心,分支界限)

文章目录 问题描述暴力枚举回溯法动态规划法贪心法分支界限法 问题描述 假设有一个货郎担要拜访n个城市&#xff0c;他必须选择所要走的路程&#xff0c;路程的限制时每个城市只能拜访一次&#xff0c;而且最后要走到原来出发的城市&#xff0c;要求路径长度。 旅行商问题将要…

为销售赋能:利用 Splashtop 增强远程培训技术

远程销售团队这一概念在当今快节奏的商业环境中日益普遍。各公司正在计划在不同地点灵活开展销售业务&#xff0c;希望利用技术优势缩小地域差距。但是&#xff0c;这种向远程销售的转型面临着重大挑战&#xff0c;尤其在培训和发展领域。培训远程销售团队需要采用创新方法&…

常见树种(贵州省):012茶、花椒、八角、肉桂、杜仲、厚朴、枸杞、忍冬

摘要&#xff1a;本专栏树种介绍图片来源于PPBC中国植物图像库&#xff08;下附网址&#xff09;&#xff0c;本文整理仅做交流学习使用&#xff0c;同时便于查找&#xff0c;如有侵权请联系删除。 图片网址&#xff1a;PPBC中国植物图像库——最大的植物分类图片库 一、茶 灌…

鸿蒙 ark ui 轮播图实现教程

前言&#xff1a; 各位同学有段时间没有见面 因为一直很忙所以就没有去更新博客。最近有在学习这个鸿蒙的ark ui开发 因为鸿蒙不是发布了一个鸿蒙next的测试版本 明年会启动纯血鸿蒙应用 所以我就想提前给大家写一些博客文章 效果图 具体实现 我们在鸿蒙的ark ui 里面列表使…

土地利用数据技术服务

一、背景介绍 土地是人类赖以生存与发展的重要资源和物质保障&#xff0c;在“人口&#xff0d;资源&#xff0d;环境&#xff0d;发展&#xff08;PRED&#xff09;”复合系统 中&#xff0c;土地资源处于基础地位。随着现代社会人口的不断增长以及工业化、城市化进程的加速&a…

Excel使用VLOOKUP查询数据

VLOOKUP函数在百度百科中的解释是&#xff1a; 解释一下&#xff0c;函数需要4个参数&#xff1a; 参数1&#xff08;lookup_value&#xff09;&#xff1a;需要匹配的值参数2&#xff08;table_array&#xff09;&#xff1a;在哪个区域里进行匹配参数3&#xff08;col_index…

Dubbo3使用Zookeeper作为注册中心的方案讨论!详解DubboAdmin与PrettyZoo来监控服务的优劣!

文章目录 一&#xff1a;Dubbo注册中心的基本使用 二&#xff1a;Zookeeper注册中心的使用 1&#xff1a;依赖引入 2&#xff1a;实际开发 三&#xff1a;Zookeeper作为注册中心的使用展示 1&#xff1a;启动注册Zookeeper服务 2&#xff1a;引入注册中心 (一)&#xf…

Java 21增强对Emoji表情符号的处理了

现一个 Java 21 中有意思的东西&#xff01; 在java.Lang.Character类中增加了用于确定字符是否为 Emoji 表情符号的 API&#xff0c;主要包含下面六个新的静态方法&#xff1a; public static boolean isEmoji(int codePoint) {return CharacterData.of(codePoint).isEmoji(…

操作系统 day13(RR、优先级调度)

RR&#xff08;时间片轮转&#xff09; 响应时间&#xff1a;系统中有10个进程正在并发执行&#xff0c;如果时间片为1秒&#xff0c;则一个进程被响应可能需要等待9秒。也就是说&#xff0c;如果用户在自己进程的时间片外通过键盘发出调试命令&#xff0c;可能需要等待9秒才能…

中断方式的数据接收

中断接收简介 回顾之前的代码 之前的代码是 等待标志位RXNE位为1才有数据 进而读取数据存放在变量c中 再根据c变量的数据是为0还是为1进而编写灯亮灭的代码 if语句 但这样的代码明显不符合裸机多任务的编程模型 因为在while中为进程 进程执行的时间不能大于5ms 但是while&…

Qt/QML编程学习之心得:一个Qt工程的学习笔记(九)

1、.pro文件 加CONFIG += c++11,才可以使用Lamda表达式(一般用于connect的内嵌槽函数) 2、QWidget 这是Qt新增加的一个类,基类,窗口类,QMainWindow和QDialog都继承与它。 3、Main函数 QApplication a应用程序对象,有且仅有一个 a.exec() 进行消息循环、阻塞 MyWi…

《图解Java数据结构与算法:微课视频版》简介

本书系统、全面地介绍数据结构的基础理论与算法设计&#xff0c;精选数据结构考研习题和各类典型例题进行讲解&#xff0c;案例和课后习题丰富&#xff0c;突出对数据结构算法实践能力的培养。本书算法均采用Java语言实现&#xff0c;示例代码可直接上机运行。 本书配套资源丰…

Spring-jdbcTemplate-配置数据库连接池,配置文件方式beans.xml

1、jdbc.properties jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql:///studb jdbc.userroot jdbc.pwd123456 2、beans.xml <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans&…

Python BDD 框架比较之 pytest-bdd vs behave

pytest-bdd和behave是 Python 的两个流行的 BDD 测试框架&#xff0c;两者都可以用来编写用户故事和可执行的测试用例&#xff0c; 具体选择哪一个则需要根据实际的项目状况来看。 先简单看一下两者的功能&#xff1a; pytest-bdd 基于pytest测试框架&#xff0c;可以与pytest…

港口大型设备状态监测及预测性维护策略

在现代港口运营中&#xff0c;大型设备的正常运行对于保障港口作业的高效性至关重要。为了实现设备的可靠性和持续性&#xff0c;港口管理者需要采取一系列状态监测和预测性维护策略。 推进自动化和智能化是提高港口大型设备状态监测和维护管理效率的重要途径。通过应用先进的…

【计算机网络笔记】数据链路层概述

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…

读像火箭科学家一样思考笔记07_探月思维

1. 挑战“不可能”的科学与企业 1.1. 互联网 1.1.1. 和电网一样具有革命性&#xff0c;一旦你插上电源&#xff0c;就能让自己的生活充满活力 1.1.2. 互联网的接入可以帮助人们摆脱贫困&#xff0c;拯救生命 1.1.3. 互联网还可以提供与天气相关的信息 1.2. 用廉价、可靠的…

Windows如何截取屏幕图片以及动态图

在制作PPT或是其他演示文稿或是说明文档的时候&#xff0c; 常常需要截取网页或是屏幕的截图&#xff0c;在Windows中有多种方式可以实现截取屏幕。 Windows 截取屏幕图片的方式 在Windows 中截取屏幕中某个区块的方式有&#xff1a; 方式1. 最原始的方式&#xff1a; 点击 …

C练习题_2

一、单项选择题(本大题共20小题,每小题2分,共40分。在每小题给出的四个备选项中选出一个正确的答案&#xff0c;并将所选项前的字母填写在答题纸的相应位置上。&#xff09; 以下叙述中错误的是&#xff08;) A.对于double类型数组&#xff0c;不可以直接用数组名对数组进行整…