Java并发--ReentrantLock原理详解
ReentrantLock是什么?
- ReentrantLock重入锁,递归无阻塞的同步机制,实现了Lock接口;
- 能够对共享资源重复加锁,即当前线程获取该锁,再次获取不会被阻塞;
- 支持公平锁和非公平锁。
UML图
- 公平锁
问题:
- 重入性的实现原理;
- 公平和非公平锁。
ReentrantLock的使用
//非公平锁
ReentrantLock lock = new ReentrantLock();
lock.lock();
//lock方法:
public void lock() {
sync.lock();
}
- Sync为ReentrantLock里面的一个内部类,它继承AQS(AbstractQueuedSynchronizer),它有两个子类:公平锁FairSync和非公平锁NonfairSync。
- ReentrantLock里面大部分的功能都是委托给Sync来实现的,同时Sync内部定义了lock()抽象方法由其子类去实现,默认实现了nonfairTryAcquire(int acquires)方法,可以看出它是非公平锁的默认实现方式。下面我们看非公平锁的lock()方法:
final void lock() {
//尝试获取锁
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
//获取失败,调用AQS的acquire(int arg)方法
acquire(1);
}
- 首先会第一次尝试快速获取锁,如果获取失败,则调用acquire(int arg)方法,该方法定义在AQS中,如下:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
- 这个方法首先调用tryAcquire(int arg)方法,在AQS中讲述过,tryAcquire(int arg)需要自定义同步组件提供实现,非公平锁实现如下:
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
//当前线程
final Thread current = Thread.currentThread();
//获取同步状态
int c = getState();
//state == 0,表示没有该锁处于空闲状态
if (c == 0) {
//获取锁成功,设置为当前线程所有
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//线程重入
//判断锁持有的线程是否为当前线程
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
释放锁
- 获取同步锁后,使用完毕则需要释放锁,ReentrantLock提供了unlock释放锁:
public void unlock() {
sync.release(1);
}
- unlock内部使用Sync的release(int arg)释放锁,release(int arg)是在AQS中定义的:
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
- 与获取同步状态的acquire(int arg)方法相似,释放同步状态的tryRelease(int arg)同样是需要自定义同步组件自己实现:
protected final boolean tryRelease(int releases) {
//减掉releases
int c = getState() - releases;
//如果释放的不是持有锁的线程,抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//state == 0 表示已经释放完全了,其他线程可以获取同步状态了
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
只有当同步状态彻底释放后该方法才会返回true。当state == 0 时,则将锁持有线程设置为null,free= true,表示释放成功。
- 在线程获取锁时,如果已经获取锁的线程是当前线程,则直接再次获取成功;
- 锁会被获取n次,只有锁在被释放同样n次之后,该锁才会被完全释放。
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair tryfor trylock method.
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 如果该锁未被任何线程占有,该锁能被当前线程获取
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//如果该锁被占有,检查占有线程是否是当前线程
else if (current == getExclusiveOwnerThread()) {
//如果是当前线程,再次获取,计数加一
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
注释:如果该锁未被任何线程占有,该锁能被当前线程获取,如果该锁被占有,检查占有线程是否是当前线程,如果是的话,同步状态加一并返回true。
重入锁--非公平锁--解锁的实现原理
protected final boolean tryRelease(int releases) {
//当前线程的同步状态减一
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
//只用同步状态为0的时候,锁才能释放成功,返回true
free = true;
setExclusiveOwnerThread(null);
}
//当锁未完全释放,返回false
setState(c);
return free;
}
注释:
- ReentrantLock的构造方法,无参时是构造非公平锁,源码为:
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}
- ReentrantLock的构造方法,带boolean参数,可传入一个boolean值,true时为公平锁,false时为非公平锁,源码为:
/**
* Creates an instance of {@codeReentrantLock} with the
* given fairness policy.
*
* @paramfair {@codetrue} ifthis lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
重入锁--公平锁--加锁的实现原理
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
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;
}
注释:
public final boolean hasQueuedPredecessors() {
Node t = tail; //尾节点
Node h = head; //头节点
Node s;
//头节点 != 尾节点
//同步队列第一个节点不为null
//当前线程是同步队列第一个节点
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
//该方法主要做一件事情:主要是判断当前线程是否位于CLH同步队列中的第一个。如果是则返回true,否则返回false。
- 代码和非公平锁基本一直,唯一的不同在于增加了hasQueuedPredecessors的逻辑判断。该方法用来判断当前节点在同步队列中是否有前驱节点。如果有前驱节点说明有线程比当前线程更早的请求资源,根据公平性,当前线程请求资源失败。如果当前节点没有前驱节点,猜能够做后面的逻辑判断,
- 公平锁每次都是从同步队列中的对一个节点获取到锁,而非公平性锁则不一定,有可能刚释放锁的线程能够再次获取到锁。
- 公平锁每次获取到锁为同步队列中的第一个节点,保证请求资源时间上的绝对顺序,而非公平锁有可能刚释放锁的线程下次继续获取该锁,则有可能导致其他线程永远无法获取到锁,造成“饥饿”现象。
- 公平锁为了保证时间上的绝对顺序,需要频繁的上下文切换,而非公平锁会降低一定的上下文切换,降低性能开销。因此,ReentrantLock默认选择的是非公平锁,则是为了减少一部分上下文切换,保证了系统更大的吞吐量。
Java并发--ReentrantLock原理详解的更多相关文章
- java并发编程 | 锁详解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock
原文:java并发编程 | 锁详解:AQS,Lock,ReentrantLock,ReentrantReadWriteLock 锁 锁是用来控制多个线程访问共享资源的方式,java中可以使用synch ...
- Java并发关键字Volatile 详解
Java并发关键字Volatile 详解 问题引出: 1.Volatile是什么? 2.Volatile有哪些特性? 3.Volatile每个特性的底层实现原理是什么? 相关内容补充: 缓存一致性协议 ...
- java的ReentrantLock类详解
ReentrantLock 能用于更精细化的加锁的Java类, 通过它能更清楚了解Java的锁机制 ReentrantLock 类的集成关系有点复杂, 既有内部类, 还有多重继承关系 类的定义 pub ...
- JAVA线程池原理详解二
Executor框架的两级调度模型 在HotSpot VM的模型中,JAVA线程被一对一映射为本地操作系统线程.JAVA线程启动时会创建一个本地操作系统线程,当JAVA线程终止时,对应的操作系统线程也 ...
- Java虚拟机工作原理详解 (一)
一.类加载器 首先来看一下java程序的执行过程. 从这个框图很容易大体上了解java程序工作原理.首先,你写好java代码,保存到硬盘当中.然后你在命令行中输入 javac YourClassNam ...
- Java虚拟机工作原理详解
原文地址:http://blog.csdn.net/bingduanlbd/article/details/8363734 一.类加载器 首先来看一下java程序的执行过程. 从这个框图很容易大体上了 ...
- java并发编程 | 线程详解
个人网站:https://chenmingyu.top/concurrent-thread/ 进程与线程 进程:操作系统在运行一个程序的时候就会为其创建一个进程(比如一个java程序),进程是资源分配 ...
- [转]java虚拟机工作原理详解
一.类加载器 首先来看一下java程序的执行过程. 从这个框图很容易大体上了解java程序工作原理.首先,你写好java代码,保存到硬盘当中.然后你在命令行中输入 javac YourClassNam ...
- JAVA线程池原理详解一
线程池的优点 1.线程是稀缺资源,使用线程池可以减少创建和销毁线程的次数,每个工作线程都可以重复使用. 2.可以根据系统的承受能力,调整线程池中工作线程的数量,防止因为消耗过多内存导致服务器崩溃. 线 ...
随机推荐
- Security8:权限模拟
用户可以模拟其他用户或登陆的权限来执行查询,并且在查看用户和登录的权限时,结果会受到模拟上下文的影响.当执行EXECUTE AS命令时,原始用户的安全上下文会进行切换,除了ORIGINAL_LOGIN ...
- DOM变化后事件绑定失效
第一个file在change时,是能够触发事件的,而第二插入的file则没有change事件.对于这个问题,有如下两种解决方法: 第一种是将绑定change事件封装成一个函数,在点击button按钮插 ...
- JSP页面基础用法和方法查询
导包jar:jstl.jar standard.jar jsp嵌套java代码,使用jsp脚本:1)<%java代码%> ----- 内部的java代码翻译到service方法的内部 ...
- mysql中 Rank、DENSE_RANK()的区别
相同点:RANK()和DENSE_RANK()的是排名函数 不同点:RANK()是跳跃排序,即如果有两条记录重复,接下来是第三级别 如:1 2 2 4,会跳过3 DENSE_RANK()是连续排序,即 ...
- Ubuntu虚拟机的安装
1.在VMware中新建虚拟机 注意W10的现在强制升级VMware所以大家,安装低版本的时候提示,升级就区官网下载一个新版本的即可. 这边大家填写自己的就好,要记住自己填写的! 这边我们等一会即可. ...
- Longest Mountain in Array 数组中的最长山脉
我们把数组 A 中符合下列属性的任意连续子数组 B 称为 “山脉”: B.length >= 3 存在 0 < i < B.length - 1 使得 B[0] < B[1] ...
- python 比较常见的工具方法
下面是一些工作过程中比较常见的工具方法,但不代表最终答案.希望能对你有所帮助,如果您有更好更多的方法工具,欢迎推荐! 1. 按行读取带json字符串的文件 # -*- coding:utf-8 -*- ...
- Charles截获iPhone网络请求
Charles介绍:Charles是在Mac下常用的截取网络封包的工具,在做iOS开发时,有时为了调试与服务器端的网络通讯协议,常常需要服务端原因一起调试.有了Charles客户端人员自娱自乐了,想怎 ...
- 搭建SpringCloud微服务框架:一、结构和各个组件
搭建微服务框架(结构和各个组件) 简介 SQuid是基于Spring,SpringBoot,使用了SpringCloud下的组件进行构建,目的是想搭建一套可以快速开发部署,并且很好上手的一套微服务框架 ...
- Rocket - debug - TLDebugModuleInner - ABSTRACTAUTO
https://mp.weixin.qq.com/s/adSB7lmKcqmwVd80-gmdIw 简单介绍TLDebugModuleInner中ABSTRACTAUTO寄存器的实现. 1. ABST ...