ReentrantLock源码分析
属性
重入锁是基于AQS实现的,它提供了公平锁和非公平锁两个版本的实现。
public class ReentrantLock implements Lock, java.io.Serializable { /***************公平锁和非公平锁*****************/ //锁(该类核心)
private final Sync sync;
//非公平锁版本
static final class NonfairSync extends Sync{……}
//公平锁版本
static final class FairSync extends Sync{……} /**
* 默认使用非公平锁
*/
public ReentrantLock() {
sync = new NonfairSync();
} /**
* 手动指定公平策略
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
} }
非公平锁和公平锁两个类都是内部类,而ReentrantLock中只引入Sync类型的sync,面向接口编程。实际使用哪个版本则由构造器决定,默认使用非公平锁,也可以在构造时手动指定。
构造器
/**
* 构造器:默认非公平,等价于ReentrantLock(false)
*/
public ReentrantLock() {
//默认使用非公平锁
sync = new NonfairSync();
} /**
* 构造器:使用指定的公平策略
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
加锁(非公平版)-lock
因为默认使用非公平版本,我们先跟踪一下非公平版的lock()源码,看看是如何实现的
public void lock() {
sync.lock();
} //内部类NonfairSync中的lock方法
final void lock() {
//先使用CAS方式【抢占一次】锁。若成功则独占该锁(将AQS的state由0改为1,并将当前线程设置锁拥有者)
if (compareAndSetState(0, 1))
//将当前线程设置为锁拥有者(exclusiveOwnerThread表示“持有锁的线程”)
setExclusiveOwnerThread(Thread.currentThread());
else//失败,则加入等待队列(入队前会先进行一次获取锁操作)
acquire(1);
} //父类AQS类中的acquire方法
public final void acquire(int arg) {
//入队前,此时锁可能已被释放,先尝试一次获取锁的操作。
//获取失败,则将线程包装成节点,加入等待队列尾部,完成后中断线程。
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
} protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
} final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
//锁没被线程持有,则竞争该锁,成功则将state由0改为1,并将当前线程标记为锁拥有者
if (c == 0) {
if (compareAndSetState(0, acquires)) {//CAS方式获取锁
//将当前线程标记为锁拥有者
setExclusiveOwnerThread(current);
return true;
}
}//锁已经被持有
//锁已被线程持有,则统计重入次数。
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;//state值+1(传进来的是1)
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
//修改state【不需要使用CAS来修改state,相当于偏向锁】
setState(nextc);
return true;
}
return false;
}
加锁(公平版)-lock
再来跟踪一下公平版本的lock()方法源码
/**
* 没有抢占操作,直接进入等待队列排队(公平)
*/
final void lock() {
acquire(1);
} //父类AQS类中的acquire方法
public final void acquire(int arg) {
//入队前,此时锁可能已被释放,先尝试一次获取锁的操作。
//获取失败,则将线程包装成节点,加入等待队列尾部,完成后中断线程。
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
} /**
* tryAcquire公平版本。
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
//锁没被线程持有
if (c == 0) {
//【快捷方式】先判断是否有前驱节点(因为队列是FIFO,前驱等待时间更长,既然公平,就要保证先来先获取)
//若无前驱,则通过CAS操作获取,成功则将state由0改为1,并设置当前线程拥有该锁。
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;
}
释放锁-unlock
unlock方法实现都是相同的。
public void unlock() {
sync.release(1);
} protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//锁状态已为0,则可以释放锁了。(state累加了多少次,就要对应的减多少次,才能把锁解开)
if (c == 0) {
free = true;//释放锁成功
setExclusiveOwnerThread(null);//设置锁拥有者为null
}
//更新state
setState(c);
return free;
}
总结
1.锁的状态变化?
锁由state表示,初始状态为0。线程初次获取到锁,则将state由0改为1,锁拥有者为当前线程。线程重入获取到锁,依旧将state状态加1,每次重入都加1。
退出临界区state就减1,最终直至0,锁释放,锁拥有者为null。
2.非公平锁获取锁和公平获取锁过程的区别?
非公平lock:
①先进行一次CAS抢占获取锁,成功则返回,失败则进入等待队列。
②入队列前,可能此时锁已被释放,先进行一次CAS获取锁,成功则返回。失败将线程封装成节点加入队列尾部,并中断线程。
公平lock:
①直接进入等待队列。
②入队列前,可能此时锁已被释放,先进行一次获取锁操作。过程是:判断是否有前驱节点,如果有前驱,根据FIFO必然前驱更应优先获取锁,因此获取锁失败。若无前驱,则再通过CAS获取锁,成功则返回,失败将线程封装成节点加入队列尾部,并中断线程。
参考:
五月的仓颉 ReentrantLock实现原理深入探究
活在梦里 AQS源码解读
ReentrantLock源码分析的更多相关文章
- Java并发编程-ReentrantLock源码分析
一.前言 在分析了 AbstractQueuedSynchronier 源码后,接着分析ReentrantLock源码,其实在 AbstractQueuedSynchronizer 的分析中,已经提到 ...
- java多线程---ReentrantLock源码分析
ReentrantLock源码分析 基础知识复习 synchronized和lock的区别 synchronized是非公平锁,无法保证线程按照申请锁的顺序获得锁,而Lock锁提供了可选参数,可以配置 ...
- ReentrantLock源码分析--jdk1.8
JDK1.8 ArrayList源码分析--jdk1.8LinkedList源码分析--jdk1.8HashMap源码分析--jdk1.8AQS源码分析--jdk1.8ReentrantLock源码分 ...
- JUC AQS ReentrantLock源码分析
警告⚠️:本文耗时很长,先做好心理准备,建议PC端浏览器浏览效果更佳. Java的内置锁一直都是备受争议的,在JDK1.6之前,synchronized这个重量级锁其性能一直都是较为低下,虽然在1.6 ...
- ReentrantLock 源码分析以及 AQS (一)
前言 JDK1.5 之后发布了JUC(java.util.concurrent),用于解决多线程并发问题.AQS 是一个特别重要的同步框架,很多同步类都借助于 AQS 实现了对线程同步状态的管理. A ...
- JUC之ReentrantLock源码分析
ReentrantLock:实现了Lock接口,是一个可重入锁,并且支持线程公平竞争和非公平竞争两种模式,默认情况下是非公平模式.ReentrantLock算是synchronized的补充和替代方案 ...
- Java并发编程之ReentrantLock源码分析
ReentrantLock介绍 从JDK1.5之前,我们都是使用synchronized关键字来对代码块加锁,在JDK1.5引入了ReentrantLock锁.synchronized关键字性能比Re ...
- Java并发系列[5]----ReentrantLock源码分析
在Java5.0之前,协调对共享对象的访问可以使用的机制只有synchronized和volatile.我们知道synchronized关键字实现了内置锁,而volatile关键字保证了多线程的内存可 ...
- Java并发编程 ReentrantLock 源码分析
ReentrantLock 一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大. 这个类主要基于AQS(Abst ...
- concurrent(三)互斥锁ReentrantLock & 源码分析
参考文档:Java多线程系列--“JUC锁”02之 互斥锁ReentrantLock:http://www.cnblogs.com/skywang12345/p/3496101.html Reentr ...
随机推荐
- Linux文件管理命令 cat
1.cat 命令:将文件内容连接后传送到标准输出或重定向到文件. 1)命令语法格式:cat [OPTION] [FILE]... 2)命令选项参数说明如下所示. -n(number):从第一行开始对文 ...
- Python3 Selenium多窗口切换
Python3 Selenium多窗口切换 以腾讯网(http://www.qq.com/)为例,打开腾讯网,点击新闻,打开腾讯新闻,点击新闻中第一个新闻链接. 在WebDriver中封装了获取当前窗 ...
- AppiumLibrary常用关键字
通过上一章节,open application关键字的使用,相信大家对手机自动化充满了兴趣,那么今天这一章节,主要介绍AppiumLibrary中常用关键字的使用. 一.实用函数 关键字 含义 实例 ...
- Shell编程基础知识(一)
一.基本的运行Linux程序的3种方法: (1) 使文件具有可执行权限,直接运行文件.eg: chmod a+x testfile.sh ./testfile.sh (2) 直接调用命令解释器来 ...
- 4.8Python数据处理篇之Matplotlib系列(八)---Figure的学习
目录 目录 前言 (一)figure()方法的定义 (二)figure()方法的参数 (三)figure()方法的例子 1.多窗体绘图: 2.窗口得分别率 目录 前言 今天我们来学习一下plt.fig ...
- WPF设计の自定义窗体
效果图如下: 实现思路: 1.继承Window类 2.为自定义的CustomWindow类设计窗体样式(使用Blend很方便!) 3.为窗体增加最大最小化和关闭按钮,并实现鼠标拖拽改变窗体大小(使用D ...
- focus()无效问题
我想点击弹出一个控件的时候控件里的input自动获取焦点,INPUT的class为"group-input",可用下面的写法不行: document.queryselect(&qu ...
- python 中的__init__.py的用法与个人理解
使用Python模块常见的情况是,事先写好A.py文件,需要import B.py文件时,先拷贝到当前目录,然后再import 这样的做法在程序量较小的情况下是可行的,如果程序交互复杂程度稍高,就很费 ...
- 转://Oracle not in查不到应有的结果(NULL、IN、EXISTS详解)
问题: 语句1 : Select * from table1 A where A.col1 not in ( select col1 from table2 B ) ...
- ldconfig 的简单用法
ldconfig: configure dynamic linker run-time binds. 比如,我编译完 boost 后(boost 的有些库需要编译后生成动态或静态库使用,例如 Pyth ...