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 ...
随机推荐
- Windows SDK 8安装失败的绕坑办法
安装win sdk 8,提示错误:管道正在被关闭. 查看安装log文件,有如下错误: Error 0x800700e8: Failed to write message type to pipe.Er ...
- nmap脚本使用总结
0x00 前言: nmap的基本介绍和基本使用方法,在乌云知识库中已经有人提交过,讲的比较详细,在此文中就不再讲述. 具体链接:http://drops.wooyun.org/tips/2002 本文 ...
- js判断是否是移动端自动跳转到wap页面代码
<script type="text/javascript"> function is_mobile(){ var regex_match=/(nokia|iphone ...
- linux学习笔记整理(九)
第十章 Centos7-系统进程管理本节所讲内容:10.1 进程概述和ps查看进程工具10.2 uptime查看系统负载-top动态管理进程10.3 前后台进程切换- nice进程优先级-实战scre ...
- 2.05-random-uesr-proxy
import urllib.request def proxy_user(): proxy_list = [ {"https":""}, # {"ht ...
- Python中关于if __name__=='__main__'的问题
先举一个简单的例子: 这里有两个.py的文件,a.py和b.py a.py内容为: b.py的内容为: 当执行b.py时结果为: 也就是将a.py文件里的内容执行一边,b.py里的内容执行一边,这显然 ...
- php微信生成微信公众号二维码扫描进入公众号带参数
https://blog.csdn.net/qq_22823581/article/details/80248555 <?php namespace app\api\model; set_tim ...
- 日幣匯率 ( Node-Red 爬蟲 )
https://tutorials.webduino.io/zh-tw/docs/socket/useful/exchange-node-red.html
- P3374 【模板】树状数组 1--洛谷luogu
题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某一个数加上x 2.求出某区间每一个数的和 输入输出格式 输入格式: 第一行包含两个整数N.M,分别表示该数列数字的个数和操作的总个数. ...
- 【Codeforces Round 650】Codeforces #334 (Div. 1)
模拟CF650,ABC三题,RK90 Codeforces 650 A 思路:首先看式子 \(\sqrt{(x_i-x_j)^2+(y_i-y_j)^2}=|x_i-x_j|+|y_i-y_j|\) ...