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 ...
随机推荐
- PHP is much better than you think
Rants about PHP are everywhere, and they even come from smart guys.When Jeff Atwood wrote yet anothe ...
- Nginx location配置详解
上一篇博客Nginx配置详解已经说过了nginx 的基本配置情况,今天来详细讲述一下nginx的location的配置原则, location是根据Uri来进行不同的定位,location可以把网站的 ...
- LeetCode算法题-Implement Queue Using Stacks(Java实现)
这是悦乐书的第195次更新,第201篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第57题(顺位题号是232).使用栈实现队列的以下操作. push(x) - 将元素x推 ...
- Win10 开始运行不保存历史记录原因和解决方法
Win10 开始运行命令以后,再次打开就没有任何历史记录了,常规方法是桌面-右键-个性化-开始-显示最常用的应用..可是打开是灰色的不可选. 每次打开开始都没有以前的记录..比如需要打开下regedi ...
- 【Teradata】配置PE和AMP(congfig和reconfig工具、vprocmanager)
The Reconfiguration and Configuration utilities are used to define the AMPs and PEs that operate tog ...
- JSP内置九个对象Request请求对象
jsp内置对象是什么呢? 例如Java语言使用一个对象之前需要实例化(也就是所说的new一个对象),创建对象这个过程有点麻烦,所以在jsp中提供了一些内置对象,用来实现很多jsp应用.在使用内置对象时 ...
- 看Linux 之父是如何定义 Linux?
看Linux 之父是如何定义 Linux? LINUX是什么? LINUX是一个免费类unix内核,适用于386-AT计算机,附带完整源代码.主要让黑客.计算机科学学生使用,学习和享受.它大部分用C编 ...
- 7.02-bs4_btc
import requests from bs4 import BeautifulSoup from lxml import etree import json class BtcSpider(obj ...
- 如何利用pip自动生成和安装requirements.txt依赖
在查看别人的Python项目时,经常会看到一个requirements.txt文件,里面记录了当前程序的所有依赖包及其精确版本号.这个文件有点类似与Rails的Gemfile.其作用是用来在另一台PC ...
- java xml文件中相同Id遍历
import java.io.File;import java.util.List;import org.dom4j.Document;import org.dom4j.DocumentExcepti ...