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 ...
随机推荐
- Docker 入门到实践(四)Docker 使用镜像
一.获取镜像 Docker Hub 上有大量的高质量的镜像让我们获取,命令为: docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签] 具体的选项可以 ...
- 4.11Python数据处理篇之Matplotlib系列(十一)---图例,网格,背景的设置
目录 目录 前言 (一)图例legend 1.默认不带参数的图例 2.添加参数的图例 3.将图例移动到框外 (二)网格grid 1.说明 2.源代码: 3.输出效果 (三)背景axses 1.设置全局 ...
- Day 1 For Knowledge Management
Hi, There: This is my first day to use CNblogs as my personal knowledge management on internet. I wa ...
- qt designer设置界面是label中文字与文本框对齐设置
往往在使用 qt designer布置界面时,添加的label和文本框中是直接从工具箱中拖进去的,由于每个控件尺寸大小不一,就会造成label中的文字相对于文本框比较较偏上,看下面未经调整的直接效果 ...
- 寒假特训——搜索——H - Nephren gives a riddle
What are you doing at the end of the world? Are you busy? Will you save us? Nephren is playing a gam ...
- python抓取月光博客的全部文章而且依照标题分词存入mongodb中
猛击这里:python抓取月光博客的全部文章
- Jmeter插件安装及使用
1 安装Plugins Manager插件 1.1 下载Plugins Manager插件 插件下载官方地址:https://jmeter-plugins.org/downloads/all/ 将下载 ...
- 洛谷P2982 [USACO10FEB]慢下来Slowing down
题目 题目大意 :给出一棵树,节点有点权,求每个节点的祖先中点权小于该节点的结点的个数 . 思路如下 : 从根节点开始,对树进行深度优先遍历. 当进行到节点 i 时,有: $\text{i}$ 的祖 ...
- python入门学习:2.列表简介
python入门学习:2.列表简介 关键点:列表 2.1 列表是什么2.2 修改.添加和删除元素2.3 组织列表 2.1 列表是什么 列表,是由一系列按特定顺序排列的元素组成.你可以创建包含字母表 ...
- ASP.Net:Javascript 通过PageMethods 调用后端WebMethod方法 + 多线程数据处理 示例
ASP.Net:Javascript 通过PageMethods 调用后端WebMethod方法 + 多线程数据处理 示例 2012年04月27日 16:59:16 奋斗的小壁虎 阅读数:4500 ...