前言
ReentrantLock作为Java并发包显示锁的典型实现,又被称作可重入的独占锁,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁定相同的一些基本行为和语义,等同于synchronized的使用,但是ReentrantLock提供了比synchronized更强大、灵活的锁机制,可以减少死锁发生的概率。
因为ReentrantLock是直接实现Lock接口的显示锁,所以它不但实现了阻塞式获取同步锁的方法,而且还实现了非阻塞的、响应超时或中断获取同步锁的功能,跟进一步的是,ReentrantLock还实现了对公平锁(FairSync)和非公平锁(NonfairSync)的支持,我们可以在使用的时候根据需要选择使用不同的公平策略,公平锁与非公平锁的区别在于公平锁的锁获取是有顺序的。但是公平锁的效率往往没有非公平锁的效率高,在许多线程访问的情况下,公平锁表现出较低的吞吐量。
ReentrantLock重入锁源码分析
有了前面对AQS源码的分析,以及如何自定义同步器的学习,我们再来分析ReentrantLock的源码就显得完全不值一提,因为它完全就是按照我们之前自定义同步器的方式编写的,所不同的是它同时实现了公平锁与非公平锁两种机制,并额外提供了很多查询同步队列状态的方法供开发者使用而已。
从上面的ReentrantLock类结构可以看出,首先ReentrantLock实现了Lock、Serializable接口,ReentrantLock的抽象静态内部类Sync继承AQS基类,再分别由静态内部类FairSync和NonfairSync实现了公平锁和非公平锁的逻辑。
构造方法
public ReentrantLock() {
sync = new NonfairSync();//默认使用非公平锁
}
public ReentrantLock(boolean fair) { //根据参数采用何种锁
sync = fair ? new FairSync() : new NonfairSync();
}
通过构造方法可以看出,ReentrantLock默认使用了非公平锁,原因无他,只是因为非公平锁的运行效率,吞吐量更高。要想使用公平锁外面只能通过参数指定。
非公平获取锁
我站在使用者的角度从最上层往下分析源码,首先从默认的非公平锁的获取操作开始。值得注意的是,对于ReentrantLock锁的获取操作在内部类Sync中被定义为了抽象方法,其具体的实现由FairSync和NonfairSync来。所以我们首先看NonfairSync对lock()方法的实现:
final void lock() {
if (compareAndSetState(0, 1)) //直接尝试获取锁,
setExclusiveOwnerThread(Thread.currentThread()); //设置当前线程为该独占锁的拥有者
else
acquire(1); //走正常流程获取锁
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires); //这里nonfairTryAcquire被定义在父类Sync中
}
从以上非公平锁在堆获取锁的方法lock()的实现可以看出,在走正常的AQS队列等待获取锁的方式之前,它会直接通过CAS快速的尝试获取同步锁,万一这时候刚好锁被其他拥有线程释放,后面等待的线程还没来得及获取锁呢,所以不公平的特性在这里也得到了体现,新的尝试获取锁的线程反而有可能会抢占等待队列中的线程获取锁的机会。
我们接着往下看,只有在直接尝试获取锁失败之后,它才会执行AQS的独占式获取锁的顶层入口方法acquire(int)方法,我们知道acquire(int)方法最终又会调用被我们覆写的tryAcquire(int)方法尝试获取锁,成功则返回,失败则加入同步等待队列被阻塞等待被前驱唤醒。所以我们接着看被我们覆写的tryAcquire()方法, 这里的tryAcquire()方法的实现其实执行的是其父类Sync中的nonfairTryAcquire(int)方法,所以我们移步到抽象父类Sync中的nonfairTryAcquire()方法。
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();//获取同步状态
if (c == 0) { //state == 0,表示没有该锁处于空闲状态
//直接尝试获取锁成功,设置为当前线程所有
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//如果占用锁的线程时当前线程则表示重入,
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires; //重入则加1(acquires固定为1)
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc); //因为不存在竞争,直接修改状态即可
return true;
}
return false;
}
由上面的代码注释已经很清楚了,所以非公平获取锁的逻辑总结如下:
- 直接通过CAS尝试修改同步状态state获取锁,成功设置当前线程为锁的占有者,失败走AQS的acquire(),tryAcquire()逻辑。
- 走AQS的acquire()的时候,通过调用tryAcquire()执行自定义的逻辑,即通过判断同步状态进行不同的逻辑处理。
- 如果同步状态为空闲,再次直接通过CAS尝试修改同步状态state获取锁,成功之后设置当前线程为锁的占有者,返回true.
- 如果同步状态不为空闲,但是是重入的情况,则直接对同步状态进行加1,返回true.
- 如果通过状态不为空闲,也不是重入的情况,则通过AQS独占式的内部逻辑,添加到同步等待队列,等待唤醒。
公平式获取锁
分析了默认的非公平式获取锁的过程,我们接着分析公平锁的获取,它对lock()方法的实现在FairSync内部类中:
final void lock() {
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
//同步状态位空闲时,还要判断当前同步等待队列是否存在不是当前线程的下一个等待执行的线程。
//hasQueuedPredecessors方法很简单,如果队列为空,或者当前线程是头节点的后继节点就返回false
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//重入的处理和非公平锁一样
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
通过以上公平锁的获取源码我们可以得出它的获取逻辑如下:
- 在进入同步等待队列之前,只会利用AQS的acquire(),tryAcquire()逻辑来尝试获取同步锁,而在尝试获取锁的时候,通过判断同步状态进行不同的逻辑处理
- 如果同步状态为空闲,还要对同步等待队列进行检查,如果同步队列为空,或者自己是排在第二位的代表下一个即将有资格获取锁的线程时才会通过CAS获取同步锁。
- 如果同步状态不为空闲,但是是重入的情况,则直接对同步状态进行加1,返回true.
- 其他情况都需要进入同步等待队列排队等待。
通过分析公平锁与非公平锁的获取过程可以看到,它们至少存在两个不同的地方:第一个不同点在于,非公平获取锁的过程中存在两次强行使用CAS直接获取锁的操作,在强行获取失败之后才会心甘情愿的进入FIFO的同步等待队列老老实实的排队等候,而公平锁只有在同步状态为空闲,并且同步队列为空或者自己就是下一个即将有资格尝试获取锁的线程的时候才会通过CAS进行尝试获取锁,其他情况(当然不包括重入)都会毫不犹豫的直接进入等待队列排队等候。这两个不同点也正是非公平锁与公平锁的差异,总结起来所谓的公平,只有在轮到自己的时候才做出尝试,绝不插队。而所谓的非公平,其实就是在进入睡眠等待队列之前,会不放过任何一个可以利用的空闲时机,让自己早点获取到锁。非公平锁可能会导致某个线程长时间处于饥饿中,但是极少线程切换,提升锁的获取成功率,增大系统吞吐量。
ReentrantLock锁的释放
不同于锁的获取过程,锁的释放对于公平锁和非公平锁的过程都一样,所以我们接着看看ReentrantLock的锁释放方法unlock().
public void unlock() {
sync.release(1);//直接调用的AQS独占锁释放的顶层入口方法
}
protected final boolean tryRelease(int releases) {
int c = getState() - releases; //直接减去releases
//在真正进行释放之前要进行锁的拥有者判断,如果不是当前线程在占有锁则抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) { //state ==0 表示彻底释放了,其他线程才能够获取到锁
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
锁的释放过程很简单,直接执行的AQS独占锁的锁释放顶层入口方法,通过回调自定义的tryRelease()方法真正进行释放操作。在释放之前要判断是否是当前线程占用锁,并且将重入彻底释放之后,才会清空当前锁的拥有者,并返回true, 在没有彻底释放之前,返回false.
对于 ReentrantLock的其他非阻塞式、响应超时或中断的锁获取方法,原理非常简单,就不在一一熬述。另外,ReentrantLock类还提供了一系列的对同步等待队列和条件等待队列的查询方法,这里也都不在介绍,在需要的时候可以自行查阅。
- 大小: 13.6 KB
分享到:
相关推荐
【作品名称】:基于servlet+jsp+mysql实现的影视管理系统【课程设计】 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【项目介绍】: 基于servlet+jsp+mysql实现的影视管理系统【课程设计】 基于servlet+jsp+mysql实现的影视管理系统【课程设计】 Java Web课程设计,基于servlet+jsp+ajax+mysql做的影视管理系统 运行环境: Tomcat 9.0 JDK 1.8 MySQL 8.0 后台管理账号密码均为:root,项目依赖:lib 目录 【资源声明】:本资源作为“参考资料”而不是“定制需求”,代码只能作为参考,不能完全复制照搬。需要有一定的基础看懂代码,自行调试代码并解决报错,能自行添加功能修改代码。
kernel-5.15-ky10-x86.tar.gz
【作品名称】:基于AT89C51 单片机为核心器件,程序设计采用C 语言,Keil 软件编译程序,配以相关外围接口电路,实现了方波、锯齿波、正弦波、三角波、梯形波五种特定波形的产生【论文+源码】 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【项目介绍】:本设计中的波形发生器系统要求基于51单片机,因此选用以AT89C51单片机作为整个系统的控制核心,应用其强大的接口功能,构成整个波形发生器的硬件系统。使用C 语言对单片机编程可产生相应的正弦波,方波,三角波,锯齿波梯形波波形信号。在程序运行时,当接收到按键信息后,需要输出某种波形时,调用相应的中断服务子程序和波形发生程序,经电路的数/模转换器和运算放大器处理后,从信号发生器的输出端口输出即可得到要求的波形。 当需要改变频率时只需要改变单片机的波形发生程序中的递增或者递减变量即可。 【资源声明】:本资源作为“参考资料”而不是“定制需求”,代码只能作为参考,不能完全复制照搬。需要有一定的基础看懂代码,自行调试代码并解决报错,能自行添加功能修改代码。
基于java的法律咨询系统设计与实现.docx
适用于元营销 API 的 Python SDK适用于 Python 的 Facebook Business SDK 介绍Facebook Business SDK是一站式服务,可帮助我们的合作伙伴更好地服务于他们的业务。合作伙伴正在使用多个 Facebook API 来满足其客户的需求。采用所有这些 API 并在各个平台上保持最新状态可能非常耗时,而且最终会造成高昂的成本。为此,Facebook 开发了 Business SDK,将其许多 API 捆绑到一个 SDK 中,以简化实施和维护。Business SDK 是 Marketing API SDK 的升级版,其中包括 Marketing API 以及来自不同平台(如 Pages、Business Manager、Instagram 等)的许多 Facebook API。快速入门商业SDK入门指南Python 目前是我们第三方开发人员最常用的语言。是一个 Python 包,它提供了您的 Python 应用程序与Business SDK 内的 Facebook APIfacebook_business之间的
数学建模培训资料 数学建模实战题目真题答案解析解题过程&论文报告 公交车调度的运作数学模型 共12页.pdf
smart-http 是一款可编程的 Http 应用微内核,方便用户根据自身需求进行 Server 或 Client 的应用开发。支持GET、POST的 HTTP 请求。提供了 URL 路由组件,可以快速搭建一套静态服务器。支持部分 RFC2612 规范,后续会逐渐完善。支持 Https 协议,由 smart-socket 为其赋能。具备文件上传的能力。支持 websocket、Cookie支持 Server、Client 开发
新闻资讯系统 微信小程序+SpringBoot毕业设计 源码+数据库+论文+启动教程 项目启动教程:https://www.bilibili.com/video/BV1oiBpYcEBp
高校师生工作室-JAVA-基于微信小程序的高校师生工作室管理系统的设计与实现
基于java的常见小儿疾病中医护理系统设计与实现.docx
本教程播放列表涵盖了 Python 中的数据结构和算法。每个教程都有数据结构或算法背后的理论、BIG O 复杂性分析和可供练习的练习。使用 Python 的数据结构和算法本教程涵盖了 Python 中的数据结构和算法。每个教程都包含数据结构或算法背后的理论、BIG O 复杂度分析以及可供练习的练习。要观看视频,您可以访问播放列表https://www.youtube.com/playlist?list=PLeo1K3hjS3uu_n_a__MI_KktGTLYopZ12订阅 codebasics youtube 频道https://www.youtube.com/c/codebasics
数学建模学习资料 蒙特卡罗方法课件教程 第2章.随机数 共29页.pptx
python实现基于CNN网络的新闻数据集文本分类源码+数据集(Python期末大作业),个人大三学期的期末大作业、经导师指导并认可通过的高分大作业设计项目,评审分98分。主要针对计算机相关专业的正在做大作业的学生和需要项目实战练习的学习者,可作为课程设计、期末大作业。 python实现基于CNN网络的新闻数据集文本分类源码+数据集(Python期末大作业)python实现基于CNN网络的新闻数据集文本分类源码+数据集(Python期末大作业),个人大三学期的期末大作业、经导师指导并认可通过的高分大作业设计项目,评审分98分。主要针对计算机相关专业的正在做大作业的学生和需要项目实战练习的学习者,可作为课程设计、期末大作业。python实现基于CNN网络的新闻数据集文本分类源码+数据集(Python期末大作业),个人大三学期的期末大作业、经导师指导并认可通过的高分大作业设计项目,评审分98分。主要针对计算机相关专业的正在做大作业的学生和需要项目实战练习的学习者,可作为课程设计、期末大作业。python实现基于CNN网络的新闻数据集文本分类源码+数据集(Python期末大作业),个人大
中小学知识产权教育试点学校申报表.doc
基于django的音乐推荐系统.zip
在建工程涉及专项行动情况检查表.docx
本项目是一个基于Python技术的学生管理系统,采用Django框架进行开发,旨在为计算机相关专业的学生提供一个实践性强、功能全面的管理系统,以帮助他们完成毕业设计或进行项目实战练习。 系统实现了对学生信息、课程信息、成绩、考勤等多方面的管理功能。学生信息管理包括学生基本信息的增删改查;课程信息管理允许管理员设置课程信息,包括课程名称、授课老师、学分等;成绩管理功能使学生和教师能够录入、查看和修改成绩;考勤管理则方便教师记录学生的出勤情况。 该项目采用B/S架构,前端使用HTML、CSS、JavaScript等技术,后端使用Python语言和Django框架,数据库采用MySQL。Django框架提供了强大的后台管理功能,使得系统管理更加便捷。 通过开发这个项目,学生不仅能提升自己的编程能力,还能学习到如何构建一个实际应用的系统,对于即将步入职场的学生来说,具有很高的实用价值。
适用于 Python 的 Splunk 软件开发工具包参考文档适用于 Python 的 Splunk Enterprise 软件开发工具包版本 2.1.0适用于 Python 的 Splunk Enterprise 软件开发套件 (SDK) 包含库代码,旨在使开发人员能够使用 Splunk 平台构建应用程序。Splunk 平台是一个搜索引擎和分析环境,它使用分布式 map-reduce 架构来有效地索引、搜索和处理大型时变数据集。Splunk 平台深受系统管理员的欢迎,用于聚合和监控 IT 机器数据、安全性、合规性以及各种其他场景,这些场景都需要有效地从大量时间序列数据中索引、搜索、分析和生成实时通知。Splunk 开发者平台使开发人员能够利用 Splunk 平台所使用的相同技术来构建令人兴奋的新应用程序。开始使用 Python 版 Splunk SDK开始使用 Python 版 Splunk Enterprise SDKSplunk Enterprise SDK for Python 包含库代码,其示例位于splunk-app-examples存储库
分布式事务练习
家庭财务管理系统 微信小程序+SSM毕业设计 源码+数据库+论文+启动教程 项目启动教程:https://www.bilibili.com/video/BV1BfB2YYEnS