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 ...
随机推荐
- 使用 cmd连接 Oracle,MySql,SQL Server 数据库
1. Oracle cmd连接数据库 语法: sqlplus 用户/口令(密码)@服务器IP/数据库实例名(SID) 1.1 方式一 数据库服务在本机上IP可以用localhost替换 sqlplus ...
- Ubuntu16.04下iTop4412环境搭建+Android4.0.3编译
系统:Ubuntu16.04.02(优麒麟) 安装库文件和JDK 使用cd 命令,进入解压出来的“Android_JDK” -->“jdk6”文件夹,运行脚本文件 “install-sun-ja ...
- C语言 统计一串字符中空格键、Tab键、回车键、字母、数字及其他字符的个数(Ctrl+Z终止输入)
//凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/ #include<stdio.h> void main(){ , num=, blank=, ...
- HTTP1.0 、1.1
网上有很多资料说明这个,但都很长的,觉得东西太多也记不住,就记点东西,权当笔记. HTTP 超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一 ...
- pip命令安装 pyinstaller失败解决办法
写在前面,如果是用win10系统的用户,一定要先通过管理员模式打开命令窗口,我是直接按 win + x 键,选择 "Windows Powershell(管理员)",否则会提示: ...
- 设计模式のBridgePattern(桥接模式)----结构模式
一.产生背景 这里以电视遥控器的一个例子来引出桥接模式解决的问题,首先,我们每个牌子的电视机都有一个遥控器,此时我们能想到的一个设计是——把遥控器做为一个抽象类,抽象类中提供遥控器的所有实现,其他具体 ...
- 第9章 Java中的线程池 第10章 Exector框架
与新建线程池相比线程池的优点 线程池的分类 ThreadPoolExector参数.执行过程.存储方式 阻塞队列 拒绝策略 10.1 Exector框架简介 10.1.1 Executor框架的两级调 ...
- GT常见问题
1.需要root? 大部分核心功能都需要root权限.需要root的功能有:FPS.Mem Assistant.Logcat日志.抓包.流畅度获取(未开放). 2.root工具总弹出提示框要确认roo ...
- Spring Security(十三):5.2 HttpSecurity
Thus far our WebSecurityConfig only contains information about how to authenticate our users. How do ...
- 【转】WPF PasswordBox不支持绑定解决方法
原文地址:https://www.cnblogs.com/wxjing67/p/3935717.html PasswordBox的Password属性因为安全原因不支持直接绑定,可以使用依赖属性实现. ...