ReentrantLock 源码解析
创始人
2025-06-01 05:52:58
0

前言

注:本文的源码来自 JDK11

ReentrantLock 是 Java 中的一个可重入锁,它可以用于替代传统的 synchronized 关键字来进行线程同步。
下面是与 synchronized 关键字的一些对比:

名称实现重入性中断性公平性是否支持超时释放锁
ReentrantLockJava API级别,基于AQS实现可重入可中断支持非公平与公平可以超时手动是否锁
synchronizedJVM 级别,基于对象的 monitor 对象实现可重入不可中断非公平无法设置超时自动是否锁

可以看出 ReentrantLock 提供了更多的灵活性和可扩展性,不知你是否开始对它的原理产生兴趣?

注意:你需要对 AQS 的工作原理有所了解,因为 ReentrantLock 是在 AQS 的基础上实现的。

类结构

ReentrantLock 类实现了 java.util.concurrent.locks.Lock 接口,它的内部实现包括一个 Sync 内部类,该类是ReentrantLock 的核心实现。
Sync 继承了 AbstractQueuedSynchronizer 抽象类,用于管理线程的同步状态。Sync 类有两个子类,分别是NonfairSyncFairSync,用于实现非公平锁和公平锁。UML 如下图所示:

Reentrant lock uml

Sync 类源码

由于公平锁与非公平锁是 Sync 的子类,那么我们先分析 Sync

abstract static class Sync extends AbstractQueuedSynchronizer {private static final long serialVersionUID = -5179523762034025860L;/*** 默认的非公平实现,有个细节是,为什么非公平的实现要放父类这里?* 其实 ReentrantLock 的 tryLock() 就是直接调这里的,不管你是哪个实现,都是使用非公平的实现。*/@ReservedStackAccessfinal boolean nonfairTryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();// 如果 state 为 0 表示没有线程占用锁if (c == 0) {// cas 成功表示拿到锁if (compareAndSetState(0, acquires)) {// 设置当前获取锁的线程setExclusiveOwnerThread(current);return true;}}// 因为是可重入锁,当 state 不为 0 时,再判断下如果为当前线程,则将 state 加上去,释放锁的时候再减else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;// 从这里可以知道,可重入次数是有限制的,即为 2147483647if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");// 由于是自己线程,不可能存在竞争关系,没必要用 cassetState(nextc);return true;}return false;}/*** 释放锁的逻辑,非公平与公平的都是一样的,因为已经获取到锁,因此这里的代码都线程安全的,都不用 cas*/@ReservedStackAccessprotected 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;}// 略...
}

FairSync(公平锁源码)

加锁

下面先来看公平锁的加锁逻辑,这里只需实现 tryAcquire,因为 父类 Sync 已经实现了 tryRelease 方法。

static final class FairSync extends Sync {private static final long serialVersionUID = -3000897897090466540L;/*** 公平锁的 tryAcquire 方法,这里跟非公平的实现几乎一样,只不过多了 hasQueuedPredecessors() 的判断*/@ReservedStackAccessprotected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {// hasQueuedPredecessors() 表示在当前线程之前,队列里是否还有线程等待// true:  head --> node(thread1) --> node(currenThread) // false: head --> node(currenThreaad)// 因为必须保证公平,所以只需要按照 AQS 的 FIFO 队列来就好了,当前线程没有在队头就获取不到锁返回 falseif (!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;}
}
解锁

解锁直接使用父类的,因此不用重写。

NonfairSync(非公平锁源码)

加锁

下面先来看非公平锁的加锁逻辑,非常的简洁。

static final class NonfairSync extends Sync {private static final long serialVersionUID = 7316153563782823691L;protected final boolean tryAcquire(int acquires) {// 直接使用父类的方法,在上面已经分析过了,请拉回去看看return nonfairTryAcquire(acquires);}
}
解锁

同样的解锁直接使用父类的,也不用重写。

为什么非公平锁的效率比公平锁的高?

很多时候都不推荐使用公平锁的方式去加锁,都说使用非公平锁的效率要比公平锁的要好,但是为什么呢?
从上面的源码来看,公平锁也仅仅是多了 hasQueuedPredecessors() 的判断,其实在唤醒队列中阻塞的线程时涉及到上下文切换,这需要一定的时间消耗,多个线程的耗时累积起来效率自然就低了。
要尽量地减少这个上下文切换的时间也很简单,直接让当前的线程去抢锁,因为当前线程就在用户态,不会有上下文切换这个耗时,效率自然就好了。

总结

本文对比了 ReentrantLocksynchronized 的区别,知道 ReentrantLock 在用法上更加的灵活;分析了ReentrantLock 公平锁与非公平锁的实现,发现代码是非常简单的,这都得益于 AQS 这个抽象同步框架,其实它们的主要区别是在加锁的时候会不会尝试获取锁;最后思考了为何非公平锁的效率会比公平锁的高。

相关内容

热门资讯

工地食品安全责任书 工地食品安...   为保障施工人员的身体健康与生命安全,加强施工工地的食品安全管理,明确各自的责任防止食物中毒事故的...
医院单位计划生育责任书 计划生...   为了认真贯彻落实国家计划生育法规、《XX省人口与计划生育条例》,形成全社会齐抓共管、综合治理人口...
学校单位计划生育责任书 单位计...   为了认真贯彻国家《人口与计划生育法》、《XX省人口与计划生育条例》,确保学校年度人口和计划生育工...
机关单位计划生育责任书 单位计...   根据《县委办公室县政府办公室关于印发〈XX县20___年度县直有关部门人口和计划生育目标管理责任...
工程质量目标责任书范本 工程质...   为进一步贯彻企业法人质量思想,规范施工质量行为,强化工程质量管理,增强质量意识、品牌意识和市场竞...
校园消防安全知识宣传标语 消防...   学校消防宣传教育的对象是包括中小学生、大学生等等在内的各年龄段、各文化层次的学生。因此,在具体工...
教师岗位目标责任书范本 教师安...   为办好人民满意的学校,每位教师必须按照学校“依法治校树正气、立足校本保生存,科学管理求发展,求真...
最新或2023(历届)公务员事...  最新或2023(历届)春节放假安排:  最新或2023(历届)2月7日至最新或2023(历届)2月...
创建文明城区标语 创建文明城区... 创建文明城区,必将有力地促进我区四个文明的协调发展,也有助于进一步提高群众的生活质量,实现幸福追赶,...
医院义诊活动标语横幅 义诊活动...   医院开展义诊活动,在服务百姓健康的同时,不仅可以提升医院品牌形象,还可以构建和谐医患关系,拉近医...
预防出生缺陷宣传标语 预防出生...   全面推进出生缺陷综合防治工作,宣传普及优生科学知识,在全社会营造重视和关注出生缺陷防治工作的良好...
大学创意迎新横幅标语 高一新生...   相比以往新生入学毫无新意可言的传统标语显得过时,让人感觉麻木死板。墨守成规的传统标语早已不能勾起...
校园消防安全警示语 校园消防安...   校园是目前涉及社会面最广泛的场所之一,那里既是先进思潮的发源地,也是公益思想传播的载体。学生、学...
婚育新风进万家活动标语 婚育新...   在全国特别是广大农村,大力宣传晚婚晚育、少生优生、生男生女一样好、女儿也是传后人、男女平等和计划...
保护绿地告示牌标语 保护绿地告...   启用绿地告示牌,不但可以帮助人们树立起良好的环境保护意识,还可以让人们认识到“爱绿护绿”的重要性...
爱绿护绿宣传语 爱绿护绿宣传语...   以植树节为契机,在广大中小学生中开展形式多样的“爱绿、护绿、亲绿”实践体验活动,积极参与宣传,开...
植绿护绿标语 植绿护绿标语 植...   绿色就是环保,绿色就是低碳,绿色就是生命,植绿护绿更需爱绿。用镜头记录植绿护绿中的永恒瞬间,弘扬...
赠医生锦旗标语 赠医生锦旗标语... 无论你在何方,无论你在何处,只要你身穿那件白色的工作服,你一直保持着微笑。不论辛苦,不论忙碌,你的微...
赠学校老师锦旗用语 赠学校老师... 你的一双手,绘就了学生们成长的乐章;你的一双眼,演绎了人生的悲欢离合;你的一双脚,踏平了我们前行的道...
德育教育宣传标语 德育教育宣传...   学校进行德育教育工作,它能使学生形成一定的思想,法纪和道德素质,这些方面的优良品德,不论是对学生...