JUC第十三讲:JUC锁: ReentrantLock详解
JUC第十三讲:JUC锁: ReentrantLock详解
本文是JUC第十三讲,JUC锁:ReentrantLock详解。可重入锁 ReentrantLock 的底层是通过 AbstractQueuedSynchronizer 实现,所以先要学习上一章节 AbstractQueuedSynchronizer 详解。
文章目录
- JUC第十三讲:JUC锁: ReentrantLock详解
- 1、带着BAT大厂的面试问题去理解
- 2、ReentrantLock源码分析
- 2.1、类的继承关系
- 2.2、类的内部类
- 2.3、类的属性
- 2.4、类的构造函数
- 2.5、核心函数分析
- 3、示例分析
- 3.1、公平锁
- 4、参考文章
1、带着BAT大厂的面试问题去理解
请带着这些问题继续后文,会很大程度上帮助你更好的理解相关知识点。
- 什么是可重入,什么是可重入锁? 它用来解决什么问题? 一定程度避免死锁
- ReentrantLock的核心是AQS,那么它怎么来实现的,继承吗? 说说其类内部结构关系。独占模式
- ReentrantLock是如何实现公平锁的?
- ReentrantLock是如何实现非公平锁的?
- ReentrantLock默认实现的是公平还是非公平锁? 非公平
- 使用ReentrantLock实现公平和非公平锁的示例?
- ReentrantLock和Synchronized的对比?
2、ReentrantLock源码分析
2.1、类的继承关系
ReentrantLock实现了Lock接口,Lock接口中定义了lock与unlock相关操作,并且还存在newCondition方法,表示生成一个条件。
public class ReentrantLock implements Lock, java.io.Serializable
2.2、类的内部类
ReentrantLock总共有三个内部类,并且三个内部类是紧密相关的,下面先看三个类的关系。
说明:ReentrantLock类内部总共存在Sync、NonfairSync、FairSync三个类,NonfairSync与FairSync类继承自Sync类,Sync类继承自 AbstractQueuedSynchronizer 抽象类。下面逐个进行分析。
- Sync类
Sync类的源码如下:
abstract static class Sync extends AbstractQueuedSynchronizer {// 序列号private static final long serialVersionUID = -5179523762034025860L;// 获取锁abstract void lock();// 非公平方式获取final boolean nonfairTryAcquire(int acquires) {// 当前线程final Thread current = Thread.currentThread();// 获取状态int c = getState();// 表示没有线程正在竞争该锁if (c == 0) {// 比较并设置状态成功,状态0表示锁没有被占用if (compareAndSetState(0, acquires)) {// 设置当前线程独占setExclusiveOwnerThread(current); return true; // 成功}}// 当前线程拥有该锁else if (current == getExclusiveOwnerThread()) {// 增加重入次数int nextc = c + acquires; if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");// 设置状态setState(nextc); // 成功return true; }// 失败return false;}// 实现AQS提供的拓展点// 试图在共享模式下获取对象状态,此方法应该查询是否允许它在共享模式下获取对象状态,如果允许,则获取它protected final boolean tryRelease(int releases) {int c = getState() - releases;// 当前线程不为独占线程if (Thread.currentThread() != getExclusiveOwnerThread())throw new IllegalMonitorStateException(); // 抛出异常// 释放标识boolean free = false; if (c == 0) {free = true;// 已经释放,清空独占setExclusiveOwnerThread(null); }// 设置标识setState(c); return free; }// 判断资源是否被当前线程占有protected final boolean isHeldExclusively() {// While we must in general read state before owner,// we don't need to do so to check if current thread is ownerreturn getExclusiveOwnerThread() == Thread.currentThread();}// 新生一个条件final ConditionObject newCondition() {return new ConditionObject();}// Methods relayed from outer class// 返回资源的占用线程final Thread getOwner() {return getState() == 0 ? null : getExclusiveOwnerThread();}// 返回状态final int getHoldCount() {return isHeldExclusively() ? getState() : 0;}// 资源是否被占用final boolean isLocked() {return getState() != 0;}/*** Reconstitutes the instance from a stream (that is, deserializes it).*/// 自定义反序列化逻辑private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {s.defaultReadObject();setState(0); // reset to unlocked state}
}
Sync类存在如下方法和作用如下。
- NonfairSync类
NonfairSync类继承了Sync类,表示采用非公平策略获取锁,其实现了Sync类中抽象的lock方法,源码如下:
// 非公平锁
static final class NonfairSync extends Sync {// 版本号private static final long serialVersionUID = 7316153563782823691L;// 获得锁final void lock() {// 比较并设置状态成功,状态0表示锁没有被占用if (compareAndSetState(0, 1))// 把当前线程设置独占了锁setExclusiveOwnerThread(Thread.currentThread());else // 锁已经被占用,或者set失败// 以独占模式获取对象,忽略中断acquire(1);}protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);}
}
说明:从lock方法的源码可知,每一次都尝试获取锁,而并不会按照公平等待的原则进行等待,让等待时间最久的线程获得锁。
- FairSync类
FairSync类也继承了Sync类,表示采用公平策略获取锁,其实现了Sync类中的抽象lock方法,源码如下:
// 公平锁
static final class FairSync extends Sync {// 版本序列化private static final long serialVersionUID = -3000897897090466540L;final void lock() {// 以独占模式获取对象,忽略中断acquire(1);}/*** Fair version of tryAcquire. Don't grant access unless* recursive call or no waiters or is first.*/// 尝试公平获取锁 AQS抽象类提供的拓展点protected final boolean tryAcquire(int acquires) {// 获取当前线程final Thread current = Thread.currentThread();// 获取状态int c = getState();if (c == 0) { // 状态为0// 不存在已经等待更久的线程 并且比较并且设置状态成功if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {// 设置当前线程独占setExclusiveOwnerThread(current);return true;}}// 状态不为0,即资源已经被线程占据else if (current == getExclusiveOwnerThread()) {// 下一个状态int nextc = c + acquires;if (nextc < 0) // 超过了int的表示范围throw new Error("Maximum lock count exceeded");// 设置状态setState(nextc);return true;}return false;}
}
说明:跟踪lock方法的源码可知,当资源空闲时,它总是会先判断sync队列(AbstractQueuedSynchronizer中的数据结构)是否有等待时间更长的线程,如果存在,则将该线程加入到等待队列的尾部,实现了公平获取原则。其中,FairSync类的lock的方法调用如下,只给出了主要的方法。
说明:可以看出只要资源被其他线程占用,该线程就会添加到sync queue中的尾部,而不会先尝试获取资源。这也是和Nonfair最大的区别,Nonfair每一次都会尝试去获取资源,如果此时该资源恰好被释放,则会被当前线程获取,这就造成了不公平的现象,当获取不成功,再加入队列尾部。
2.3、类的属性
ReentrantLock类的sync非常重要,对 ReentrantLock 类的操作大部分都直接转化为对Sync和 AbstractQueuedSynchronizer 类的操作。
public class ReentrantLock implements Lock, java.io.Serializable {// 序列号private static final long serialVersionUID = 7373984872572414699L; // 同步队列private final Sync sync;
}
2.4、类的构造函数
- ReentrantLock() 型构造函数
默认是采用的非公平策略获取锁
public ReentrantLock() {// 默认非公平策略sync = new NonfairSync();
}
- ReentrantLock(boolean) 型构造函数
可以传递参数确定采用公平策略或者是非公平策略,参数为true表示公平策略,否则,采用非公平策略:
public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();
}
2.5、核心函数分析
通过分析ReentrantLock的源码,可知对其操作都转化为对Sync对象的操作,由于Sync继承了AQS,所以基本上都可以转化为对AQS的操作。如将ReentrantLock的lock函数转化为对Sync的lock函数的调用,而具体会根据采用的策略(如公平策略或者非公平策略)的不同而调用到Sync的不同子类。
所以可知,在ReentrantLock的背后,是AQS对其服务提供了支持,由于之前我们分析AQS的核心源码,遂不再累赘。下面还是通过例子来更进一步分析源码。
3、示例分析
3.1、公平锁
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;class MyThread extends Thread {private Lock lock;public MyThread(String name, Lock lock) {super(name);this.lock = lock;}public void run () {lock.lock();try {System.out.println(Thread.currentThread() + " running");try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}} finally {lock.unlock();}}
}public class AbstractQueuedSynchronizerDemo {public static void main(String[] args) throws InterruptedException {Lock lock = new ReentrantLock(true);MyThread t1 = new MyThread("t1", lock); MyThread t2 = new MyThread("t2", lock);MyThread t3 = new MyThread("t3", lock);t1.start();t2.start(); t3.start();}
}
运行结果(某一次):
Thread[t1,5,main] running
Thread[t2,5,main] running
Thread[t3,5,main] running
说明: 该示例使用的是公平策略,由结果可知,可能会存在如下一种时序。
说明: 首先,t1线程的lock操作 -> t2线程的lock操作 -> t3线程的lock操作 -> t1线程的unlock操作 -> t2线程的unlock操作 -> t3线程的unlock操作。根据这个时序图来进一步分析源码的工作流程。
- t1线程执行lock.lock,下图给出了方法调用中的主要方法。
说明: 由调用流程可知,t1线程成功获取了资源,可以继续执行。
- t2线程执行 lock.lock,下图给出了方法调用中的主要方法。
说明: 由上图可知,最后的结果是t2线程会被禁止,因为调用了LockSupport.park。
- t3线程执行lock.lock,下图给出了方法调用中的主要方法。
说明:由上图可知,最后的结果是t3线程会被禁止,因为调用了LockSupport.park。
- t1线程调用了lock.unlock,下图给出了方法调用中的主要方法。
说明:如上图所示,最后,head的状态会变为0,t2线程会被unpark,即t2线程可以继续运行。此时t3线程还是被禁止。
- t2获得cpu资源,继续运行,由于t2之前被park了,现在需要恢复之前的状态,下图给出了方法调用中的主要方法。
说明:在setHead函数中会将head设置为之前head的下一个结点,并且将pre域与thread域都设置为null,在acquireQueued返回之前,sync queue就只有两个结点了。
- t2执行lock.unlock,下图给出了方法调用中的主要方法。
说明: 由上图可知,最终unpark t3线程,让t3线程可以继续运行。
- t3线程获取cpu资源,恢复之前的状态,继续运行。
说明: 最终达到的状态是sync queue中只剩下了一个结点,并且该节点除了状态为0外,其余均为null。
- t3执行lock.unlock,下图给出了方法调用中的主要方法。
说明: 最后的状态和之前的状态是一样的,队列中有一个空节点,头节点为尾节点均指向它。
使用公平策略和Condition的情况可以参考上一篇关于AQS的源码示例分析部分,不再累赘。
4、参考文章
- 【JUC】JDK1.8源码分析之ReentrantLock(三)
相关文章:
JUC第十三讲:JUC锁: ReentrantLock详解
JUC第十三讲:JUC锁: ReentrantLock详解 本文是JUC第十三讲,JUC锁:ReentrantLock详解。可重入锁 ReentrantLock 的底层是通过 AbstractQueuedSynchronizer 实现,所以先要学习上一章节 AbstractQueuedSynchronizer 详解。 文章目录 …...
WSL2安装历程
WLS2安装 1、系统检查 安装WSL2必须运行 Windows 10 版本 2004 及更高版本(内部版本 19041 及更高版本)或 Windows 11。 查看 Windows 版本及内部版本号,选择 Win R,然后键入winver。 2、家庭版升级企业版 下载HEU_KMS_Activ…...
Ubuntu20配置Mysql常用操作
文章目录 版权声明ubuntu更换软件源Ubuntu设置静态ipUbuntu防火墙ubuntu安装ssh服务Ubuntu安装vmtoolsUbuntu安装mysql5.7Ubuntu安装mysql8.0Ubuntu卸载mysql 版权声明 本博客的内容基于我个人学习黑马程序员课程的学习笔记整理而成。我特此声明,所有版权属于黑马程…...
【解决方案】‘create’ is not a member of ‘cv::aruco::DetectorParameters’
‘create’ is not a member of ‘cv::aruco::DetectorParameters’ 在构建AruCo标定板标定位姿代码的过程中,发现代码中认为create并不是aruco::DetectorParameters的成员函数,这是因为在4.7.0及以上的OpenCV版本中,对ArUco的代码做调整&…...
门牌制作(蓝桥杯)
门牌制作 题目描述 本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。 小蓝要为一条街的住户制作门牌号。 这条街一共有 2020 位住户,门牌号从 1 到 2020 编号。 小蓝制作门牌的方法是先制作 0 到 9 这几个数字字…...
支付宝支付模块开发
生成二维码 使用Hutool工具类生成二维码 引入对应的依赖 <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.5</version> </dependency><dependency><groupId>com.go…...
12、Kubernetes中KubeProxy实现之iptables和ipvs
目录 一、概述 二、iptables 代理模式 三、iptables案例分析 四、ipvs案例分析 一、概述 iptables和ipvs其实都是依赖的一个共同的Linux内核模块:Netfilter。Netfilter是Linux 2.4.x引入的一个子系统,它作为一个通用的、抽象的框架,提供…...
从0开始python学习-29.selenium 通过cookie信息进行登录
1. 手动输入cookie信息保持登录状态 url https://test.com/login driver.get(url) # 手动将cookie信息写入(有多个的情况需要分开写入)--弊端为需要每次都手动输入,很麻烦不适用 driver.add_cookie({"name": "SIAM_IMAGE_…...
CentOS安装OpenNebula(二)
被控端部署: 先要配置好yum源: [rootmaster yum.repos.d]# vim opennebula.repo[rootmaster yum.repos.d]# cat opennebula.repo [opennebula] nameopennebula baseurlhttps://downloads.opennebula.org/repo/5.6/CentOS/7/x86_64 enabled1 gpgkeyhttps…...
力扣第239题 c++滑动窗口经典题 单调队列
题目 239. 滑动窗口最大值 困难 提示 队列 数组 滑动窗口 单调队列 堆(优先队列) 给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口中的…...
华为云云耀云服务器L实例评测|华为云云耀云服务器docker部署srs,可使用HLS协议
华为云云耀云服务器L实例评测|华为云云耀云服务器docker部署srs,可使用HLS协议 什么是华为云云耀云L实例 云耀云服务器L实例,面向初创企业和开发者打造的全新轻量应用云服务器。提供丰富严选的应用镜像,实现应用一键部署&#x…...
jira流转issue条目状态transitions的rest实用脚本,issue状态改变调整
官方文档链接地址: POST Transition issue Performs an issue transition and, if the transition has a screen, updates the fields from the transition screen. sortByCategory To update the fields on the transition screen, specify the fields in the fiel…...
JAVA 注解
1 概念 Annotation(注解)是 Java 提供的一种对元程序中元素关联信息和元数据(metadata)的途径和方法。Annatation(注解)是一个接口,程序可以通过反射来获取指定程序中元素的 Annotation 对象,然后通过该 An…...
C++面试题准备
文章目录 一、线程1.什么是进程,线程,彼此有什么区别?2.多进程、多线程的优缺点3.什么时候用进程,什么时候用线程4.多进程、多线程同步(通讯)的方法5.父进程、子进程的关系以及区别6.什么是进程上下文、中断上下文7.一…...
使用Java操作Redis
要在Java程序中操作Redis可以使用Jedis开源工具。 一、jedis的下载 如果使用Maven项目,可以把以下内容添加到pom中 <!-- https://mvnrepository.com/artifact/redis.clients/jedis --> <dependency> <groupId>redis.clients</groupId>…...
VRRP配置案例(路由走向分析,端口切换)
以下配置图为例 PC1的配置 acsw下行为access口,上行为trunk口, 将g0/0/3划分到vlan100中 <Huawei>sys Enter system view, return user view with CtrlZ. [Huawei]sysname acsw [acsw] Sep 11 2023 18:15:48-08:00 acsw DS/4/DATASYNC_CFGCHANGE:O…...
【图像处理】【应用程序设计】加载,编辑和保存图像数据、图像分割、色度键控研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
05. 机器学习入门 - 动态规划
文章目录 从一个案例开始动态规划 Hi, 你好。我是茶桁。 咱们之前的课程就给大家讲了什么是人工智能,也说了每个人的定义都不太一样。关于人工智能的不同观点和方法,其实是一个很复杂的领域,我们无法用一个或者两个概念确定什么是人工智能&a…...
【JVM】第五篇 垃圾收集器G1和ZGC详解
导航 一. G1垃圾收集算法详解1. 大对象Humongous说明2. G1收集器执行一次GC运行的过程步骤3. G1垃圾收集分类4. G1垃圾收集器参数设置5. G1垃圾收集器的优化建议6. 适合使用G1垃圾收集器的场景?二. ZGC垃圾收集器详解1. NUMA与UMA2. 颜色指针3. ZGC的运作过程4. ZGC垃圾收集器…...
嵌入式Linux应用开发-基础知识-第十九章驱动程序基石⑤
嵌入式Linux应用开发-基础知识-第十九章驱动程序基石⑤ 第十九章 驱动程序基石⑤19.9 mmap19.9.1 内存映射现象与数据结构19.9.2 ARM架构内存映射简介19.9.2.1 一级页表映射过程19.9.2.2 二级页表映射过程 19.9.3 怎么给APP新建一块内存映射19.9.3.1 mmap调用过程19.9.3.2 cach…...
数据分析技能点-独立性检验拟合优度检验
在这个数据驱动的时代,数据分析已经成为了一个不可或缺的工具,无论是在商业决策、医疗研究还是日常生活中。然而数据分析并不仅仅是一堆数字和图表;它是一个需要严谨的科学方法和逻辑推理的过程。 本文将重点介绍两种广泛应用于数据分析的统计检验方法:独立性检验和拟合优…...
了解汽车ecu组成
常用ecu框架组成: BCM(body control module)-车身控制模块: 如英飞凌tc265芯片: 车身控制单元(BCM)适合应用于12V和24V两种电压工作环境,可用于轿车、大客车和商用车的车身控制。输入模块通过采集电路采集各路开关量和…...
用AI原生向量数据库Milvus Cloud 搭建一个 AI 聊天机器人
搭建聊天机器人 一切准备就绪后,就可以搭建聊天机器人了。 文档存储 机器人需要存储文档块以及使用 Towhee 提取出的文档块向量。在这个步骤中,我们需要用到 Milvus。 安装轻量版 Milvus Lite,使用以下命令运行 Milvus 服务器: (chatbot_venv) [egoebelbecker@ares milvus_…...
【OpenCV-Torch-dlib-ubuntu】Vm虚拟机linux环境摄像头调用方法与dilb模型探究
前言 随着金秋时节的来临,国庆和中秋的双重喜庆汇聚成一片温暖的节日氛围。在这个美好的时刻,我们有幸共同迎来一次长达8天的假期,为心灵充电,为身体放松,为未来充实自己。今年的国庆不仅仅是家国团聚的时刻ÿ…...
(二)详解观察者模式
一.使用场景 当我们需要一个类,在他的内部元素发生变化的时候可以主动通知其他类的时候,同时要保持良好的可拓展性,可以采用观察者模式。 二.核心 观察者模式出版者订阅者 我们拥有一个主题对象,和一些其他对象,包…...
嵌入式Linux应用开发-基础知识-第十九章驱动程序基石④
嵌入式Linux应用开发-基础知识-第十九章驱动程序基石④ 第十九章 驱动程序基石④19.7 工作队列19.7.1 内核函数19.7.1.1 定义 work19.7.1.2 使用 work:schedule_work19.7.1.3 其他函数 19.7.2 编程、上机19.7.3 内部机制19.7.3.1 Linux 2.x的工作队列创建过程19.7.3…...
2023 彩虹全新 SUP 模板,卡卡云模板修复版
2023 彩虹全新 SUP 模板,卡卡云模板,首页美化,登陆页美化,修复了 PC 端购物车页面显示不正常的问题。 使用教程 将这俩个数据库文件导入数据库; 其他的直接导入网站根目录覆盖就好; 若首页显示不正常&a…...
【AI视野·今日NLP 自然语言处理论文速览 第四十一期】Tue, 26 Sep 2023
AI视野今日CS.NLP 自然语言处理论文速览 Tue, 26 Sep 2023 Totally 75 papers 👉上期速览✈更多精彩请移步主页 Daily Computation and Language Papers Physics of Language Models: Part 3.1, Knowledge Storage and Extraction Authors Zeyuan Allen Zhu, Yuanz…...
【iptables 实战】05 iptables设置网络转发实验
一、网络架构 实验效果,通过机器B的转发功能,将机器A的报文转发到机器C 本实验准备三台机器分别配置如下网络 机器A ip:192.168.56.104 机器C ip:10.1.0.10 机器B 两张网卡,分别的ip是192.168.56.106和10.1.0.11 如图所示 如下图所示 二、…...
pygame - 贪吃蛇小游戏
蛇每吃掉一个身体块,蛇身就增加一个长度。为了统一计算,界面的尺寸和游戏元素的位置都是身体块长度的倍数 1. 上下左右方向键(或者ASDW键)控制蛇的移动方向 2. 空格键暂停和继续蛇的身体图片文件,复制到项目的asset\im…...
做网站用哪里的服务器比较好/百度推广投诉中心
【单选题】与文件系统阶段相比,关系数据库技术的数据管理方式具有许多特点,但不包括【填空题】输入矩阵 A[3,2,5;8,-5,7;-1,4,9] ,使用全下标方式用 A(2,2) 取出元素 ,使用单下标方式用 取出元素 “-5 ” 。【单选题】如果要回滚一个事务,则要使用( )语句【单选题】SELECT语句的…...
中华人民共和国建设厅官方网站/网络营销与策划试题及答案
从效率考虑:cookie > memcache > 数据库cookie对服务器端负载没影响,如果加密、解密会多消耗一点点cpu。带宽倒是会消耗得多一点,同域名下的所有http request header都会附带cookie,所以在大流量下,把js、css、图…...
中国空间站视频/社会新闻热点事件
SetCapture函数功能:该函数在属于当前线程的指定窗口里设置鼠标捕获。一旦窗口捕获了鼠标,所有鼠标输入都针对该窗口,无论光标是否在窗口的边界内。同一时刻只能有一个窗口捕获鼠标。如果鼠标光标在另一个线程创建的窗口上,只有当…...
虎门英文网站建设/爱站网长尾关键词
点击蓝字关注我们什么是函数的返回值?C语言函数如果执行成功的话,返回1、返回0还是返回别的值?如果大家还不是很清楚,那么就让小橙同学来给大家介绍一下。C语言简介首先,简单介绍一下C语言。C 语言是一种通用的、面向过…...
做网站专家/seo网站优化技术
Google Colab,全名Colaboratory,是由谷歌提供的免费的云平台,可以使用pytorch、keras、tensorflow等框架进行深度学习。其GPU为Tesla T4 GPU,有很强的算力,对于刚入门机器学习或深度学习的用户,这个平台是不…...
wordpress安装知更鸟主题/seo网站推广优化论文
In [24]: s4[s4 > 9000]Out[24]:数学 9001.0dtype: float64Series就先简要写到这,下面看pandas的另一种数据结构DataFrame.DataFrameDataFrame 是一种二维的数据结构,非常接近于电子表格或者类似 mysql 数据库的形式。它的竖行称之为 columns…...