教你完全理解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 相关文章推荐
实例讲解JQuery中this和$(this)区别
Dec 08 Javascript
jQuery实现tab选项卡效果的方法
Jul 08 Javascript
jQuery实现验证年龄简单思路
Feb 24 Javascript
Angularjs中$http以post请求通过消息体传递参数的实现方法
Aug 05 Javascript
使用JQuery选择HTML遍历函数的方法
Sep 17 Javascript
d3.js中冷门却实用的内置函数总结
Feb 04 Javascript
AngularJS的脏检查深入分析
Apr 22 Javascript
Vue2.0+ElementUI实现表格翻页的实例
Oct 23 Javascript
Vue项目中ESlint规范示例代码
Jul 04 Javascript
vscode调试node.js的实现方法
Mar 22 Javascript
js禁止查看源文件屏蔽Ctrl+u/s、F12、右键等兼容IE火狐chrome
Oct 01 Javascript
Vue中nprogress页面加载进度条的方法实现
Nov 13 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
乱谈我对耳机、音箱的感受
2021/03/02 无线电
PHP4 与 MySQL 数据库操作函数详解
2006/10/09 PHP
简单实用的.net DataTable导出Execl
2013/10/28 PHP
PHP获取网站中各文章的第一张图片的代码示例
2016/05/20 PHP
PHP实现求连续子数组最大和问题2种解决方法
2017/12/26 PHP
PHP 二维array转换json的实例讲解
2018/08/21 PHP
Thinkphp 框架扩展之数据库驱动常用方法小结
2020/04/23 PHP
如何通过Apache在本地配置多个虚拟主机
2020/07/29 PHP
php中yar框架实例用法讲解
2020/12/27 PHP
动态的改变IFrame的高度实现IFrame自动伸展适应高度
2012/12/28 Javascript
JS图像无缝滚动脚本非常好用
2014/02/10 Javascript
浅谈JavaScript Math和Number对象
2015/01/26 Javascript
在JS方法中返回多个值的方法汇总
2015/05/20 Javascript
浅谈javascript中的call、apply、bind
2016/03/06 Javascript
JavaScript中for循环的几种写法与效率总结
2017/02/03 Javascript
JS图片预加载插件详解
2017/06/21 Javascript
vue.js数据绑定的方法(单向、双向和一次性绑定)
2017/07/13 Javascript
使用Bootstrap和Vue实现用户信息的编辑删除功能
2017/10/25 Javascript
Javascript中JSON数据分组优化实践及JS操作JSON总结
2017/12/22 Javascript
用Node提供静态文件服务的方法
2018/07/06 Javascript
简单了解Javscript中兄弟ifream的方法调用
2019/06/17 Javascript
p5.js实现故宫橘猫赏秋图动画
2019/10/23 Javascript
微信小程序webSocket的使用方法
2020/02/20 Javascript
浅谈Python对内存的使用(深浅拷贝)
2018/01/17 Python
python3.5基于TCP实现文件传输
2020/03/20 Python
python读写csv文件方法详细总结
2019/07/05 Python
Python字节单位转换(将字节转换为K M G T)
2021/03/02 Python
详解HTML5布局和HTML5标签
2020/10/26 HTML / CSS
Avène雅漾美国官方网站:敏感肌肤护理专家
2016/10/24 全球购物
英国独特家具和家庭用品购物网站:Cuckooland
2020/08/30 全球购物
十佳教师事迹材料
2014/01/11 职场文书
消防安全检查制度
2014/02/04 职场文书
行政工作试用期自我评价
2014/09/14 职场文书
2014年优质护理服务工作总结
2014/11/14 职场文书
JS实现数组去重的11种方法总结
2022/04/04 Javascript
python中Pyqt5使用Qlabel标签播放视频
2022/04/22 Python