教你完全理解ReentrantLock重入锁


Posted in Javascript onJune 03, 2019

1. ReentrantLock的介绍

ReentrantLock重入锁,是实现Lock接口的一个类,也是在实际编程中使用频率很高的一个锁,支持重入性,表示能够对共享资源能够重复加锁,即当前线程获取该锁再次获取不会被阻塞。在java关键字synchronized隐式支持重入性(关于synchronized可以看这篇文章),synchronized通过获取自增,释放自减的方式实现重入。与此同时,ReentrantLock还支持公平锁和非公平锁两种方式。

那么,要想完完全全的弄懂ReentrantLock的话,主要也就是ReentrantLock同步语义的学习:1. 重入性的实现原理;2. 公平锁和非公平锁。

2. 重入性的实现原理

要想支持重入性,就要解决两个问题:

1. 在线程获取锁的时候,如果已经获取锁的线程是当前线程的话则直接再次获取成功;

2. 由于锁会被获取n次,那么只有锁在被释放同样的n次之后,该锁才算是完全释放成功。

通过这篇文章,我们知道,同步组件主要是通过重写AQS的几个protected方法来表达自己的同步语义。

针对第一个问题,我们来看看ReentrantLock是怎样实现的,以非公平锁为例,判断当前线程能否获得锁为例,核心方法为nonfairTryAcquire:

final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
//1. 如果该锁未被任何线程占有,该锁能被当前线程获取
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//2.若被占有,检查占有线程是否是当前线程
else if (current == getExclusiveOwnerThread()) {
// 3. 再次获取,计数加一
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}

这段代码的逻辑也很简单,具体请看注释。

为了支持重入性,在第二步增加了处理逻辑,如果该锁已经被线程所占有了,会继续检查占有线程是否为当前线程,如果是的话,同步状态加1返回true,表示可以再次获取成功。

每次重新获取都会对同步状态进行加一的操作,那么释放的时候处理思路是怎样的了?(依然还是以非公平锁为例)核心方法为tryRelease:

protected final boolean tryRelease(int releases) {
//1. 同步状态减1
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
//2. 只有当同步状态为0时,锁成功被释放,返回true
free = true;
setExclusiveOwnerThread(null);
}
// 3. 锁未被完全释放,返回false
setState(c);
return free;
}

代码的逻辑请看注释,需要注意的是,重入锁的释放必须得等到同步状态为0时锁才算成功释放,否则锁仍未释放。如果锁被获取n次,释放了n-1次,该锁未完全释放返回false,只有被释放n次才算成功释放,返回true。

到现在我们可以理清ReentrantLock重入性的实现了,也就是理解了同步语义的第一条。

3. 公平锁与公平锁

ReentrantLock支持两种锁:公平锁和非公平锁。

何谓公平性,是针对获取锁而言的,如果一个锁是公平的,那么锁的获取顺序就应该符合请求上的绝对时间顺序,满足FIFO。ReentrantLock的构造方法无参时是构造非公平锁,源码为:

public ReentrantLock() {
sync = new NonfairSync();
}

另外还提供了另外一种方式,可传入一个boolean值,true时为公平锁,false时为非公平锁,源码为:

public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}

在上面非公平锁获取时(nonfairTryAcquire方法)只是简单的获取了一下当前状态做了一些逻辑处理,并没有考虑到当前同步队列中线程等待的情况。

我们来看看公平锁的处理逻辑是怎样的,核心方法为:

protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
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;
}
}

这段代码的逻辑与nonfairTryAcquire基本上一致,唯一的不同在于增加了hasQueuedPredecessors的逻辑判断,方法名就可知道该方法用来判断当前节点在同步队列中是否有前驱节点的判断,如果有前驱节点说明有线程比当前线程更早的请求资源,根据公平性,当前线程请求资源失败。如果当前节点没有前驱节点的话,再才有做后面的逻辑判断的必要性。

公平锁每次都是从同步队列中的第一个节点获取到锁,而非公平性锁则不一定,有可能刚释放锁的线程能再次获取到锁。

公平锁 VS 非公平锁

公平锁每次获取到锁为同步队列中的第一个节点,保证请求资源时间上的绝对顺序,而非公平锁有可能刚释放锁的线程下次继续获取该锁,则有可能导致其他线程永远无法获取到锁,造成“饥饿”现象。

公平锁为了保证时间上的绝对顺序,需要频繁的上下文切换,而非公平锁会降低一定的上下文切换,降低性能开销。因此,ReentrantLock默认选择的是非公平锁,则是为了减少一部分上下文切换,保证了系统更大的吞吐量。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javascript高级学习笔记整理
Aug 14 Javascript
jquery实现可点击伸缩与展开的菜单效果代码
Aug 31 Javascript
JS中多步骤多分步的StepJump组件实例详解
Apr 01 Javascript
超详细的JS弹出窗口代码大全
Apr 18 Javascript
微信小程序 scroll-view组件实现列表页实例代码
Dec 14 Javascript
深入理解JS继承和原型链的问题
Dec 17 Javascript
AngularJS中update两次出现$promise属性无法识别的解决方法
Jan 05 Javascript
jquery实现图片跟随鼠标的实例
Oct 17 jQuery
JavaScript私有变量实例详解
Jan 24 Javascript
javascript头像上传代码实例
Sep 28 Javascript
解决node终端下运行js文件不支持ES6语法
Apr 04 Javascript
在vs code 中如何创建一个自己的 Vue 模板代码
Nov 10 Javascript
生产制造追溯系统之在线打印功能
Jun 03 #Javascript
产制造追溯系统之通过微信小程序实现移动端报表平台
Jun 03 #Javascript
深入理解 JS 垃圾回收
Jun 03 #Javascript
如何让微信小程序页面之间的通信不再变困难
Jun 03 #Javascript
使用VueRouter的addRoutes方法实现动态添加用户的权限路由
Jun 03 #Javascript
使用watch在微信小程序中实现全局状态共享
Jun 03 #Javascript
深入理解JS异步编程-Promise
Jun 03 #Javascript
You might like
PHP+DBM的同学录程序(4)
2006/10/09 PHP
php stream_get_meta_data返回值
2013/09/29 PHP
jquery判断字符输入个数(数字英文长度记为1,中文记为2,超过长度自动截取)
2010/10/15 Javascript
jquery获取对象的方法足以应付常见的各种类型的对象
2014/05/14 Javascript
jQuery操作表单常用控件方法小结
2015/03/23 Javascript
jQuery实现点击按钮文字变成input框点击保存变成文字
2016/05/09 Javascript
JS弹出窗口插件zDialog简单用法示例
2016/06/12 Javascript
Node.js实现文件上传
2016/07/05 Javascript
JS获取checkbox的个数简单实例
2016/08/19 Javascript
关于Angularjs中跨域设置白名单问题
2018/04/17 Javascript
实例解析Vue.js下载方式及基本概念
2018/05/11 Javascript
webpack项目使用eslint建立代码规范实现
2019/05/16 Javascript
小程序如何使用分包加载的实现方法
2019/05/22 Javascript
vue如何在用户要关闭当前网页时弹出提示的实现
2020/05/31 Javascript
vue项目使用$router.go(-1)返回时刷新原来的界面操作
2020/07/26 Javascript
[01:03:27]Optic vs VGJ.S 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/20 DOTA
学习python的几条建议分享
2013/02/10 Python
使用C语言来扩展Python程序和Zope服务器的教程
2015/04/14 Python
Python机器学习之决策树算法实例详解
2017/12/06 Python
使用Python制作微信跳一跳辅助
2018/01/31 Python
Python使用progressbar模块实现的显示进度条功能
2018/05/31 Python
python读取一个目录下所有txt里面的内容方法
2018/06/23 Python
pyqt5 禁止窗口最大化和禁止窗口拉伸的方法
2019/06/18 Python
用python打印菱形的实操方法和代码
2019/06/25 Python
TensorFlow——Checkpoint为模型添加检查点的实例
2020/01/21 Python
浅谈Python程序的错误:变量未定义
2020/06/02 Python
英文版餐饮运营管理求职信
2013/11/06 职场文书
追悼会上的答谢词
2014/01/10 职场文书
大型活动组织方案
2014/05/10 职场文书
党员学习中共十八大报告思想汇报
2014/09/15 职场文书
表彰大会新闻稿
2015/07/17 职场文书
运动会广播稿100字
2015/08/19 职场文书
2019暑假阅读倡议书
2019/06/24 职场文书
2019通用版导游词范本!
2019/08/07 职场文书
导游词之平津战役纪念馆
2019/11/04 职场文书
Java Spring Boot请求方式与请求映射过程分析
2022/06/25 Java/Android