ReentrantLock 源码解读

news2024/10/3 2:27:58

一、ReentrantLock

ReentrantLockjava JUC 中的一个可重入锁,在上篇文章讲解 AQS 源码的时候提到 ReentrantLock 锁是基于 AQS 实现的,那是如何使用的 AQS 呢,本篇文章一起带大家看下 ReentrantLock 的源码。

AQS 中,如果需要使用AQS的特征则需要子类根据使用的场景,重写下面方法,

//查询是否正在独占资源,condition会使用
boolean isHeldExclusively()	
//独占模式,尝试获取资源,成功则返回true,失败则返回false
boolean tryAcquire(int arg)
//独占模式,尝试释放资源,成功则返回true,失败则返回false
boolean tryRelease(int arg)
//共享模式,尝试获取资源,如果返回负数表示失败,否则表示成功。
int tryAcquireShared(int arg)
//共享模式,尝试释放资源,成功则返回true,失败则返回false。
boolean tryReleaseShared(int arg)

由于这里 ReentrantLock 锁的特性,所以下面我们只需关注独占模式下的几个方法即可。

说明:本文中关于 AQS 中的方法没有做过多的解释,不了解的小伙伴可以看下这篇对 AQS 源码分析的文章,和当前文章在同一专栏:

https://blog.csdn.net/qq_43692950/article/details/129367736

下面一起开始 ReentrantLock 源码的分析:

二、ReentrantLock 的 Sync、FairSync、NonfairSync

2.1 Sync、FairSync、NonfairSync

在声明 ReentrantLock 锁时,有两种方式,一种是无参构造函数,一种则需要指定一个 fair 参数:

new ReentrantLock();
new ReentrantLock(false);

当使用无参构造函数声明时,则是创建了一个 NonfairSync 对象:

在这里插入图片描述

通过有参的构造函数,则根据传入的 fair 可以选择创建一个 FairSync 对象:

在这里插入图片描述

其实这里也不难理解 NonfairSyncFairSync 其实就是ReentrantLock 锁中的非公平锁和公平锁两种类型。

点到这两个类中,可以看到都继承自 Sync 类:

在这里插入图片描述
在这里插入图片描述

Sync 类,则继承了 AQS

在这里插入图片描述

到这里了,我们寻找几个关键的方法,在AQS中独占模式下,两大关键的方法是交由子类进行实现的,分别是 tryAcquire 尝试获取资源,和 tryRelease 尝试释放资源。

首先来看 tryAcquire 尝试获取资源:

通过 Sync 类的实现源码发现并没有重写 tryAcquire 方法,那该方法肯定在下面的子类FairSyncNonfairSync ,分别看下源码确实存在重写的方法:

在这里插入图片描述
在这里插入图片描述

2.2 NonfairSync 下的 tryAcquire

首先看下 NonfairSynctryAcquire 实现逻辑,可以看到又调用了 nonfairTryAcquire 就是 Sync 类中的 nonfairTryAcquire ,从命名上可以分析出就是非公平锁的尝试获取资源,直观就是非公平锁下获取锁操作:

在这里插入图片描述
进入到 Sync 类中的 nonfairTryAcquire中,可以看到首先获取到 AQS 中的共享资源 state,如果 state 等于 0 ,则将 state 的值修改为 acquires(默认为1,下面会分析到),并设置AQS的独占线程为当前线程,并返回 true ,说白了不就是获取到锁了吗,那就可以理解为 state 等于 0 即是无锁的状态,下面将 state 的值修改为 acquires 就是获取到锁了,改变资源的状态:

在这里插入图片描述

接着如果 state 的值不是 0 ,则当前锁已经被别的线程持有了,这里又判断了下,如果持有锁的线程正好是当前的线程,那不就是锁的重入吗,这种情况下可以直接获得锁,不过这里为了记录重入的次数,对 state 共享资源进行了 + acquires 操作,其实就是 +1 操作。

在这里插入图片描述

如果都没有成功,那此时则获取锁失败,返回 false

2.3 FairSync下的 tryAcquire

FairSync 类下的 tryAcquire 方法中,和前面 NonfairSync 类似,但不同的是,在获取到锁时,也就是拿到 state 等于 0 ,进行修改资源时,多了步 hasQueuedPredecessors 的判断:

在这里插入图片描述

下面可以进到 hasQueuedPredecessors 的方法中,可以看到是由 AQS 提供的方法,主要就是判断当前节点线程的前面是否还有等待的线程,因为 FairSync 实现的是公平锁的原则,如果当前线程前面还有等待线程,则获取锁资源也轮不到自个,让前面的老大先来:

在这里插入图片描述
hasQueuedPredecessors 方法理解后,其余的逻辑则和 NonfairSync 中的一致了。

2.4 tryRelease

到这里已经了解到了tryAcquire 尝试获取资源的逻辑,上面提到了两个重要方法,还有一个 tryRelease 没有分析逻辑,还是首先看 Sync 类中是否有重写该方法:

通过源码可以看到,在 Sync 类中就已经对 tryRelease 进行了重写,而 NonfairSyncFairSync 中都没有重写该方法,那释放资源就是走的 Sync 类下的 tryRelease 方法:
在这里插入图片描述

在该方法中,可以看到首先还是获取到了 AQS 中的 state 共享资源,然后对该资源进行 - releases (默认releases1,下面会提到 )操作,其实就是 -1 操作:

在这里插入图片描述
接着判断了下,如果当前线程不是持有锁线程,就抛出异常,也好理解,没有持有锁的线程跑过来释放锁,那肯定有问题了呀。

接着再进行判断 state 是不是等于 0 ,上面讲到在锁重入的情况下,记录重入的次数是对 state 进行 +1 操作,而这边又对 state 进行 -1 操作,如果减到最后 state 有成了最初的 0 ,那不就是重入的锁和当前持有的锁都释放完了吗,这个时候就可以将持有锁的线程置为空了,并修改最新的 state

在这里插入图片描述

看到这里就会发现获取锁和释放锁,无非就是对 AQS 中的共享资源进行操作。理解了这两大核心的方法后,下面就可以看如何运用在 ReentrantLock 中的了。

三、lock.lock()

ReentrantLock 中,需要获取锁时,直接使用 lock.lock() 即可,那 lock.lock() 到底做了什么呢,点到该方法中,可以看到是调用的 Synclock 方法,而 Sync 中的lock 方法是抽象方法,具体实现肯定在子类的 NonfairSync、 FairSync 中。

在这里插入图片描述

3.1 NonfairSync.lock()

首先看点 NonfairSync 非公平锁中的 lock 方法,直接进行了将 AQS 中的共享资源 state0 改为 1 ,如果修改成功,根据上面分析的结论不就是获取锁成功了吗,可以将AQS中的独占线程设为自己了。但是如果其他线程修改成功了,这里使用 CAS 就会修改失败,因此就会进到 acquire 方法,注意这里传递的参数默认就是 1 ,对应着前面括号中的说明:

在这里插入图片描述

acquire 方法,就是 AQS 中的独占模式获取同步资源的逻辑,会调用当前方法的 tryAcquire 尝试获取资源,如果获取不到,则加入到 AQS 的阻塞队列并阻塞挂起线程。

在这里插入图片描述

关于acquire 方法的源码解读可以参考文章开始的链接中对 AQS 源码的解读。

3.2 FairSync.lock()

FairSync 公平锁中,由于需要遵循先进先出的原则,这里没有直接已粗暴的形式对 state 进行修改,而是直接调用了 AQS 中的 acquire 方法,而 acquire 方法又会调用当前类的 tryAcquire 获取资源。

但在当前类的 tryAcquire 方法中,如果获取到了资源,会接着进行判断当前线程的前面是否还有等待的线程,如果有则让出来让别人获取资源,因此就遵循了公平锁的原则,注意这里传递的参数默认就是 1 ,同样对应着前面括号中的说明:

在这里插入图片描述
同样 tryAcquire 尝试获取资源,如果获取不到,则加入到 AQS 的阻塞队列并阻塞挂起线程。

四、lock.unlock()

上面了解到了 lock 的逻辑,既然上锁了肯定需要解锁,下面点到 unlock() 方法中,可以看到直接使用了 Syncrelease 方法释放资源,其实是 AQS 中的 release 方法,注意这里传递的参数默认就是 1 ,同样对应着前面括号中的说明:

在这里插入图片描述

AQSrelease 方法中,首先会调用 Sync 类的 tryRelease 释放资源,然后对已阻塞的线程进行唤醒:

在这里插入图片描述

关于release 方法的源码解读可以参考文章开始的链接中对 AQS 源码的解读。

五、总结

通过阅读 ReentrantLock 的源码可以发现,大量依赖于 AQS 中提供的方法,所以在阅读前一定要理解下 AQS 的作用和功能。

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

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

相关文章

linux安装influxdb-rpmyum方式

一、influxdb的安装InfluxDB简介时序数据库InfluxDB版是一款专门处理高写入和查询负载的时序数据库,用于存储大规模的时序数据并进行实时分析,包括来自DevOps监控、应用指标和IoT传感器上的数据主要特点:专为时间序列数据量身订造高性能数据存…

uniapp——ucharts的使用

ucharts是一个类似于echarts的图表框架 引入 在Hbuilder的插件下载仓库中搜索ucharts !!值得一提的是,Hbuilder的版本必须是大于3.1.0的!! 这样就导入到了项目的uni_modules里了 ucharts下载完成后,可以…

Java——异常机制

前言 随着对java的不断深入学习,在对语法以及编程思想有了一定的了解之后,在编程的过程中有可能会因为用户的输入不正确或者逻辑错误而出现异常或者错误,因此如何去捕捉与避免不应该出现的异常或者错误就变得十分重要。本文就介绍了java的异…

C++实现的二叉树创建和遍历,超入门邻家小女也懂了

目录 二叉树 特点 性质 二叉树的创建 声明 创建 -> 成员运算符 批量创建 二叉树的遍历 先序遍历 中序遍历 后序遍历 层序遍历 树的相关术语 特殊二叉树 满二叉树 完全二叉树 二叉树 树(Tree)是n(n≥0)个节点的有限集。在任意一棵…

Idea工具单工程使用卡顿设置

一、开启Idea内存占比指示 1.1、勾选Idea 内存指示器 1.2、右下角展示当前项目:内存/总内存(726/1024MB) ​​​​​​​ 二、修改Idea自定义内存 2.1、打开help - Edit Custom VM Options 2.2、自定义虚拟内存设置 -Xms1024m // 初始内存分…

【前端八股文】浏览器系列:浏览器渲染、前端路由、前端缓存(HTTP缓存)、缓存存储(HTTP缓存存储、本地存储)

文章目录渲染步骤DOM树与render树回流与重绘前端路由hash模式history模式两种模式对比前端缓存HTTP缓存强缓存协商缓存一般哪些文件对应哪些缓存HTTP缓存总结缓存存储HTTP缓存存储本地存储参考本系列目录:【前端八股文】目录总结 是以《代码随想录》八股文为主的笔记…

TTS | 语音合成论文概述

综述系列2021_A Survey on Neural Speech Synthesis论文:2106.15561.pdf (arxiv.org)论文从两个方面对神经语音合成领域的发展现状进行了梳理总结(逻辑框架如图1所示):核心模块:分别从文本分析(textanalysi…

【Linux内核一】在Linux系统下网口数据收发包的具体流向是什么?

在TCP/IP网络分层模型里,整个协议栈被分成了物理层、链路层、网络层,传输层和应用层。物理层对应的是网卡和网线,应用层对应的是我们常见的Nginx,FTP等等各种应用。Linux实现的是链路层、网络层和传输层这三层。 在Linux内核实现中…

Linux(Centos)安装Minio集群

目录1:简介2:功能与集成3:架构4:搭建集群4.1:挂载磁盘4.1.1:要求4.1.2:创建挂载目录4.1.3:注意:需要将新建的目录挂在到对应的磁盘下,磁盘不挂载好,集群启动会…

Tomcat 并发达太大导致系统崩溃解决方案

当 Tomcat 并发达太大导致系统崩溃时,可以通过以下几个步骤来解决这个问题: 1、分析原因 首先需要分析系统崩溃的原因,是因为Tomcat的性能瓶颈还是因为代码的Bug,或者是系统资源不足等等。 2、优化代码 如果是代码的问题&…

Unity Lighting -- 为场景烘焙lightmap

烘焙光照是什么? Unity中有两种不同的光照方式:实时光照和烘焙光照。 实时光照 Unity会在运行时实时计算光照。实时光源每一帧都会进行计算,这意味着它们对于场景中移动的角色和物体的响应性非常好,但它的开销也很大。 烘焙光照…

DJ1-3 操作系统引论

目录 操作系统的结构设计 1. 无结构操作系统 2. 模块化 OS 结构 3. 分层式 OS 结构 4. 微内核 OS 结构 操作系统的结构设计 操作系统是一个大型系统软件,其结构已经历了四代的变革: 第一代 OS 是无结构第二代 OS 采用模块式结构第三代是层次式结构…

韩国绿芯1~16通道触摸芯片型号推荐

随着技术的发展,触摸感应技术正日益受到更多关注和应用,目前实现触摸感应的方式主要有两种,一种是电阻式,另一种是电容式。电容式触摸具有感应灵敏、功耗低、寿命长等特点,因此逐步取代电阻式触摸,成为当前…

炫龙游戏本Win10系统总是蓝屏崩溃怎么办?

炫龙游戏本Win10系统总是蓝屏崩溃怎么办?有用户使用的炫龙游戏本最近总是在运行的过程中出现自动蓝屏的情况,有的时候自己还在操作电脑,而屏幕却蓝屏了,导致自己的工作被中断了。那么这个情况要怎么去进行修复呢?来看看…

关于centos虚拟机设置固定ip、设置后无法上网、宿主机无法ping通的解决方案

一.centos设置固定ip 1.虚拟机设置 编辑→虚拟网络编辑器,选择NAT模式。 2.NAT设置,设置网关(前面三个需要与你设置的静态虚拟机ip一致(我的是192.168.2.40))。 3.虚拟机设置为NAT模式 4.设置虚拟…

数据结构2——线性表1:基本概念

1、线性表的概念 线性表是具有相同特性的数据元素的一个有限序列。就像用细线串珠子,一个接着一个串起来。 2、线性表的逻辑特征 ① 在非空的线性表中有且只有一个开始结点a1,他没有直接前驱,而仅有一个直接后继a2,相当于a1是…

关于JS中this对象指向问题总结

一、前言 关于JS中this对象指向问题,相信做过项目的小伙伴多多少少都会遇到过,明明感觉代码写的没问题,可是运行的时候,就会报错,比如报错 xxx is not a function。 我最近也遇到了,百度学习了不少前辈对于…

Codeforces Round 703 (Div. 2)(A~D)

A. Shifting Stacks给出一个数组,每次可以将一个位置-1,右侧相邻位置1,判断是否可以经过若干次操作后使得数列严格递增。思路:对于每个位置,前缀和必须都大于该位置应该有的最少数字,即第一个位置最少是0&a…

Vue 2 组件发布到 npm

本教程使用官网教程中指示的 Rollup 作为打包工具,并尽量遵循官网教程的指引进行实践;组件项目的初始化创建方式亦是使用官网提倡的 Vue CLI 工具简便生成。另外组件打包发布到 npm 还可以使用 webpack 作为打包工具,但不在本文讨论范围。 前…

YOLOv5s网络模型讲解(一看就会)

文章目录前言1、YOLOv5s-6.0组成2、YOLOv5s网络介绍2.1、参数解析2.2、YOLOv5s.yaml2.3、YOLOv5s网络结构图3、附件3.1、yolov5s.yaml 解析表3.2、 yolov5l.yaml 解析表总结前言 最近在重构YOLOv5代码,本章主要介绍YOLOv5s的网络结构 1、YOLOv5s-6.0组成 我们熟知YO…