当前位置:网站首页>并发编程学习笔记 之 ReentrantLock实现原理的探究
并发编程学习笔记 之 ReentrantLock实现原理的探究
2022-07-29 05:20:00 【姠惢荇者】
1、前言
在前面我们已经了解到Lock接口,是对锁操作方法的一个基本定义,它提供了synchronized关键字所具备的全部功能方法,而ReentrantLock类不仅完全实现了显示锁Lock接口所定义的接口,也扩展了对使用显式锁Lock的一些监控方法。同时,我们也尝试使用ReentrantLock实现了对共享资源的同步访问,这一节我们来深入了解,这些功能是如何实现的。
2、ReentrantLock(重入锁)实现原理
2.1、ReentrantLock对接口方法的实现
通过查看ReentrantLock类的源码,我们会发现所有实现Lock接口接口的方法,包括额外增加的一些监控方式,它们其实都是借助一个Sync变量实现的,如下所示:
public void lock() {
sync.lock();
}
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
public void unlock() {
sync.release(1);
}
public Condition newCondition() {
return sync.newCondition();
}
//ReentrantLock实现的一些监控方法,同样也是借助sync实现的,代码如下(篇幅原因,仅显示了其中一个说明情况)
public int getHoldCount() {
return sync.getHoldCount();
}
//……
通过上述的部分源码,我们已经可以确定ReentrantLock类的实现,主要基于sync对象,只要我们把Sync对象研究清楚了,那么ReentrantLock类的原理我们自然而然的就搞清楚了,下面我们开始学习Sync类的实现。
2.2、ReentrantLock构造函数
ReentrantLock提供了公平锁和非公平锁的机制,主要基于Sync 的实现类FairSync和NonfairSync实现。默认构造函数使用的是非公平锁的机制,可以使用带参数的构造函数,应用公平锁机制。构造函数如下:
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
public ReentrantLock() {
sync = new NonfairSync();
}
2.2、Sync内部类
ReentrantLock对Lock的实现都是基于Sync来做的,在前面的源码已经清楚的看到了,这里我们就重点来学习Sync类的实现。
Sync类的层级结构:

根据Sync的层级结构,我们可以知道该类继承了AbstractQueuedSynchronizer(AQS)抽象类,同时有两个实现类FairSync和NonfairSync,分别应用于公平锁和非公平锁机制。其中,AbstractQueuedSynchronizer抽象类定义了一套多线程访问共享资源的同步模板,解决了实现同步器时涉及的大量细节问题,能够极大地减少实现工作,即AbstractQueuedSynchronizer为加锁和解锁过程提供了统一的模板函数,Sync类只需要实现部分抽象方法即可。
这里暂时不深入学习AbstractQueuedSynchronizer(AQS)抽象类,后续专门一篇进行介绍。
在Sync类中,主要实现了nonfairTryAcquire()和tryRelease()方法,当然,还有一些其他方法的实现, 具体请看源码的注释:
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/** * 抽象方法,在实现类FairSync和NonfairSync中实现, * 在实现类的方法中,决定了获取锁的公平锁或非公平锁的策略 */
abstract void lock();
/** * 非公平锁-尝试获取锁资源的方法,而公平锁尝试获取资源的方法却在实现类FairSync中实现。 */
final boolean nonfairTryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//获取state状态,表示锁状态,getState()方法在AbstractQueuedSynchronizer(AQS)抽象类中实现。
int c = getState();
if (c == 0) {
//等于0,表示资源可用
//cas方法设置状态,保证原子性
if (compareAndSetState(0, acquires)) {
//获取成功,设置持有锁的线程,并返回获取锁成功
setExclusiveOwnerThread(current);
return true;
}
}else if (current == getExclusiveOwnerThread()) {
//判断当前线程是否持有锁,可重入特性
//设置state的数值
int nextc = c + acquires;
if (nextc < 0) // 超过上限时
throw new Error("Maximum lock count exceeded");
//当前线程设置,不需要cas方式
setState(nextc);
return true;
}
return false;
}
/** * 释放锁资源 */
protected final boolean tryRelease(int releases) {
//计算如果释放成功后,当前线程持有的hold数量
int c = getState() - releases;
//判断是否是当前线程释放锁,不是的话,抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//如果c==0,说明释放锁成功,同时把持有锁的线程设置为null
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
//c==0时,free=true,释放锁成功,c!=0时,free=false,释放锁失败,说明有重入操作
setState(c);
return free;
}
/** * 当前线程是否持有锁 */
protected final boolean isHeldExclusively() {
return getExclusiveOwnerThread() == Thread.currentThread();
}
/** * 创建Condition对象 */
final ConditionObject newCondition() {
return new ConditionObject();
}
/** * 获取持有锁的线程 */
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
/** * 获取Hold数量,是当前线程持有锁,就返回state,否则(当前线程没有持有锁时),返回0 */
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
/** * 锁资源是否被占用 */
final boolean isLocked() {
return getState() != 0;
}
/** * 反序列化 */
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
2.3、NonfairSync实现类 (非公平锁策略)
获取锁失败的线程,会进入CLH队列阻塞,其他线程解锁会唤醒CLH队列线程,当重新竞争锁时,如果有非CLH队列的线程(还没来得及进入CLH队列阻塞的线程)参与竞争,那么就是非公平锁策略。
非公平锁策略实现类NonfairSync ,主要实现了Sync类的抽象方法lock(),在该方法中,实现了非公平锁策略;同时还实现了tryAcquire()方法(主要逻辑在Sync类的nonfairTryAcquire()方法中实现),用于获取,具体如下:
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/** * 获取锁资源,个人感觉如果发生锁竞争时,正好有线程来获取锁,是不是会比CLH阻塞队列中的线程更容易获得锁呢? */
final void lock() {
if (compareAndSetState(0, 1))//cas方法尝试设置,如果设置成功,当前线程就可以获取锁
setExclusiveOwnerThread(Thread.currentThread());
else
//在AbstractQueuedSynchronizer(AQS)抽象类中实现,执行AQS获取锁的流程
acquire(1);
}
/** * 尝试获取锁资源,在Syns类中定义nonfairTryAcquire()方法 */
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
2.4、FairSync实现类 (公平锁策略)
FairSync流程与NonfairSync类似,都是实现了lock()和tryAcquire()方法,区别在于:
- lock() 方法实现中,在FairSync中直接调用了acquire(1)方法,而在NonfairSync类中,首先进行了cas操作,然后失败后才调用了acquire(1)方法。
- 在tryAcquire()方法中,FairSync类在自己内部实现逻辑,且在在cas操作前,多了一步hasQueuedPredecessors函数,验证是否在CLH阻塞队列,而NonfairSync类中,则是调用了nonfairTryAcquire()方法,且不需要验证是否在CLH阻塞队列。
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
//直接调用AbstractQueuedSynchronizer(AQS)抽象类中acquire(),执行AQS获取锁的流程
acquire(1);
}
/** * 获取锁资源 */
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//hasQueuedPredecessors()方法在AQS抽象类中定义,验证当前现场是否在CLH阻塞队列中,不在队列中,则获取锁失败,同时还需要cas操作验证。
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;
}
}
3、总结
至此,关于ReentrantLock的实现原理我们就学习完了,但是其中涉及到的CLH阻塞队列、AQS抽象类中的hasQueuedPredecessors()、acquire()等方法,我们都没有深入学习,我们计划在下一节中,专门深入研究一下AQS的实现原理,敬请期待!!!
边栏推荐
- D3.JS 纵向关系图(加箭头,连接线文字描述)
- File permissions of day02 operation
- Flink connector Oracle CDC 实时同步数据到MySQL(Oracle12c)
- Laravel Swagger添加访问密码
- 钉钉告警脚本
- My ideal job, the absolute freedom of coder farmers is the most important - the pursuit of entrepreneurship in the future
- 华为2020校招笔试编程题 看这篇就够了(上)
- xSAN高可用—XDFS与SAN融合焕发新生命力
- 30 knowledge points that must be mastered in quantitative development [what is individual data]?
- 山寨币SHIB 在 ETH 鲸鱼的投资组合中拥有 5.486 亿美元的股份——交易者应提防……
猜你喜欢

Plato farm is expected to further expand its ecosystem through elephant swap

焕然一新,swagger UI 主题更改

改哭了,终于解决了Cannot read properties of undefined (reading ‘parseComponent‘)

Flink connector Oracle CDC 实时同步数据到MySQL(Oracle19c)

ssm整合

JDBC连接数据库详细步骤

Super simple integration of HMS ml kit to realize parent control

『全闪实测』数据库加速解决方案

“山东大学移动互联网开发技术教学网站建设”项目实训日志三

centos7 静默安装oracle
随机推荐
“山东大学移动互联网开发技术教学网站建设”项目实训日志六
How to make interesting apps for deep learning with zero code (suitable for novices)
Use of file upload (2) -- upload to Alibaba cloud OSS file server
Common prompt pop-up box of uniapp
Training log 4 of the project "construction of Shandong University mobile Internet development technology teaching website"
极致通缩和永动机模型,将推动 PlatoFarm 爆发
以‘智’提‘质|金融影像平台解决方案
加密资产熊市之下,PlatoFarm的策略玩法依旧能获得稳定收益
Training log II of the project "construction of Shandong University mobile Internet development technology teaching website"
如何在加密市场熊市中生存?
裸金属云FASS高性能弹性块存储解决方案
How can Plato obtain premium income through elephant swap in a bear market?
Study and research the way of programming
Gluster cluster management analysis
Training log 6 of the project "construction of Shandong University mobile Internet development technology teaching website"
MySQL decompressed version windows installation
Plato Farm有望通过Elephant Swap,进一步向外拓展生态
北京宝德&TaoCloud共建信创之路
“山东大学移动互联网开发技术教学网站建设”项目实训日志三
Starfish OS: create a new paradigm of the meta universe with reality as the link