彻底理解ReentrantLock
5.ReentrantLock的介绍
ReenTrantLock重入锁,是实现Lock接口的一个类,也是在实际编程中使用频率很高的一个锁,支持重入性,表示能够对共享资源能够重复加锁,即当前线程获取该锁再次获取不会被阻塞.在java关键字synchronized隐式支持重入性(关于synchro.那么nized可以看这篇文章),synchronized通过获取自增,释放自减的方式实现重入.与此同时,ReentrantLock还支持公平锁和非公平锁两种方式.那么,想要完全的弄懂ReentrantLock的话,主要也就是ReentrantLock同步语义的学习:
- 重入性的实现原理
- 公平锁和非公平锁
5.1重入性的实现原理
想要支持重锁入性,就是解决两个问题:
- 在线程获取锁的时候,如果已经获取锁的线程是当前线程的话则直接再次获取成功
- 由于锁会被获取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.到现在我们可以理解ReetrantLock重入性的实现了,也就是理解同步语义的第一条.
5.2 公平锁和非公平锁
public ReetrantLock(){
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的逻辑判断,方法名就可知道该方法用来判断当前节点在同步队列中是否有前驱动节点的判断,如果有前驱动节点说明有线程比当前线程更早的请求资源,根据公平性,当前线程请求资源失败.如果当前节点没有前驱动节点的话,再才有做后面的逻辑判断的必要性.公平锁每次都是从同步队列中的第一个节点获取到锁,而非公平性锁则不一定,有可能刚释放锁的线程能再次获取到锁.
公平锁和非公平锁
- 公平锁每次获取到锁为同步队列中的第一个节点,保证请求资源时间上的绝对顺序,而非公平锁有可能刚释放锁的线程下次继续获取该锁,则有可能导致其他线程永远无法获取锁,造成"饥饿"现象.
- 公平锁为了保证时间上的绝对顺序,需要频繁的上下文切换,而非公平锁会降低一定的上下文切换,降低性能开销.因此,ReenTrantLock默认选择的是非公平性,则是为减少一部分上下文切换,保证了系统更大的吞吐量.
如果还没理解java重入性的实现原理,可以参考这篇文章:https://blog.csdn.net/yanyan19880509/article/details/52345422/
彻底理解ReentrantLock的更多相关文章
- 深入理解ReentrantLock
在Java中通常实现锁有两种方式,一种是synchronized关键字,另一种是Lock.二者其实并没有什么必然联系,但是各有各的特点,在使用中可以进行取舍的使用.首先我们先对比下两者. 实现: 首先 ...
- 从源码角度彻底理解ReentrantLock(重入锁)
目录 1.前言 2.AbstractQueuedSynchronizer介绍 2.1 AQS是构建同步组件的基础 2.2 AQS的内部结构(ReentrantLock的语境下) 3 非公平模式加锁流程 ...
- 10.彻底理解ReentrantLock
1. ReentrantLock的介绍 ReentrantLock重入锁,是实现Lock接口的一个类,也是在实际编程中使用频率很高的一个锁,支持重入性,表示能够对共享资源能够重复加锁,即当前线程获取该 ...
- 一篇文章,彻底理解ReentrantLock
本人免费整理了Java高级资料,涵盖了Java.Redis.MongoDB.MySQL.Zookeeper.Spring Cloud.Dubbo高并发分布式等教程,一共30G,需要自己领取.传送门:h ...
- java并发系列(四)-----源码角度彻底理解ReentrantLock(重入锁)
1.前言 ReentrantLock可以有公平锁和非公平锁的不同实现,只要在构造它的时候传入不同的布尔值,继续跟进下源码我们就能发现,关键在于实例化内部变量sync的方式不同,如下所示: /** * ...
- Java多线程高并发学习笔记(二)——深入理解ReentrantLock与Condition
锁的概念 从jdk发行1.5版本之后,在原来synchronize的基础上,增加了重入锁ReentrantLock. 本文就不介绍synchronize了,有兴趣的同学可以去了解一下,本文重点介绍Re ...
- 理解ReentrantLock的公平锁和非公平锁
学习AQS的时候,了解到AQS依赖于内部的FIFO同步队列来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态等信息构造成一个Node对象并将其加入到同步队列,同时会阻塞当 ...
- Java并发编程,深入理解ReentrantLock
ReentrantLock简介 ReentrantLock重入锁,是实现Lock接口的一个类,也是在实际编程中使用频率很高的一个锁, 支持重入性,表示能够对共享资源能够重复加锁,即当前线程获取该锁再次 ...
- ReentrantLock实现原理深入探究
前言 这篇文章被归到Java基础分类中,其实真的一点都不基础.网上写ReentrantLock的使用.ReentrantLock和synchronized的区别的文章很多,研究ReentrantLoc ...
随机推荐
- .net图表之ECharts随笔07-自定义系列(多边形)
搞了一天多,才勉强搞出了一个不紧凑的六边形统计图,是真的菜. 这里ECharts的用法与06说的同一种,直接使用带all的js 先上个效果图,用面积来表示人数的多少 1. 参数option的toolt ...
- Lerning Entity Framework 6 ------ Defining the Database Structure
There are three ways to define the database structure by Entity Framework API. They are: Attributes ...
- 【BZOJ2002】 [Hnoi2010]Bounce 弹飞绵羊
BZOJ2002 [Hnoi2010]Bounce 弹飞绵羊 Solution 很早以前写的一道分块题,最近在搞LCT,又做了一遍. 1.LCT做法 看到这种动态修改,想下LCT怎么维护. 修改操作就 ...
- 文本属性和字体属性,超链接导航栏案例 background
文本属性 介绍几个常用的. 文本对齐 text-align 属性规定元素中的文本的水平对齐方式. 属性值:none | center | left | right | justify 文本颜色 col ...
- 序列化 pickle shelve json configparser
模块pickle(皮考) dumps(当破死)序列化. 把对象转化成bytes loads(楼死) 反序列化. 吧bytes转化成对象 dic = {"jay": "周杰 ...
- 跟着刚哥深入学maven(通俗易懂)
前言:目前所有的项目都在使用maven,可是一直没有时间去整理学习,这两天正好有时间,好好的整理一下. 一.为什么使用Maven这样的构建工具[why] ① 一个项目就是一个工程 如果项目非常庞大,就 ...
- [JavaScript] js实现保存文件到本地
function fake_click(obj) { var ev = document.createEvent("MouseEvents"); ev.initMouseEvent ...
- django基础之二
一.什么是架构? 框架,即framework,特指为解决一个开放性问题而设计的具有一定约束性的支撑结构,使用框架可以帮你快速开发特定的系统,简单地说,就是你用别人搭建好的舞台来做表演. 对于所有的We ...
- opencv实现canopy算法
#include "stdafx.h" using namespace cv; int main(int argc, char** argv) { Mat img=imread(& ...
- android开发学习——day2
简单了解了android stdio的操作方式,今天着手于探究活动(Activity) 了解了基本活动与手动创建活动的方法,了解了onCreate()方法,了解了创建和加载页面布局(layout) 新 ...