【java】java ReentrantLock 源码详解
文章目录
- 1.概述
- 2.问题
- 3.ReentrantLock源码分析
- 3.1 类的继承关系
- 3.2 类的内部类
- 3.2.1 Sync类
- 3.2.2 NonfairSync类
- 3.2.3 FairSyn类
- 3.3 类的属性
- 3.4 类的构造函数
- 3.5 核心函数分析
- 4.案例
- 4.1 公平锁
1.概述
案例:【Java】Java Controller 每次只能一个请求 多线程 ReentrantLock
详解参考:JUC锁: ReentrantLock详解
面经手册 · 第16篇《码农会锁,ReentrantLock之公平锁讲解和实现》
2.问题
- 什么是可重入,什么是可重入锁? 它用来解决什么问题?
- ReentrantLock的核心是AQS,那么它怎么来实现的,继承吗?
- 说说其类内部结构关系。 ReentrantLock是如何实现公平锁的?
- ReentrantLock是如何实现非公平锁的?
- ReentrantLock默认实现的是公平还是非公平锁?
- 使用ReentrantLock实现公平和非公平锁的示例?
- ReentrantLock和Synchronized的对比?
3.ReentrantLock源码分析
3.1 类的继承关系
ReentrantLock实现了Lock接口,Lock接口中定义了lock与unlock相关操作,并且还存在newCondition方法,表示生成一个条件。
public class ReentrantLock implements Lock, java.io.Serializable
3.2 类的内部类
ReentrantLock总共有三个内部类,并且三个内部类是紧密相关的,下面先看三个类的关系。
说明: ReentrantLock类内部总共存在Sync、NonfairSync、FairSync三个类
,NonfairSync与FairSync类继承自Sync类,Sync类继承自AbstractQueuedSynchronizer抽象类。下面逐个进行分析。
3.2.1 Sync类
Sync类的源码如下:
abstract static class Sync extends AbstractQueuedSynchronizer {// 序列号private static final long serialVersionUID = -5179523762034025860L;// 获取锁abstract void lock();// 非公平方式获取final boolean nonfairTryAcquire(int acquires) {// 当前线程final Thread current = Thread.currentThread();// 获取状态int c = getState();if (c == 0) { // 表示没有线程正在竞争该锁if (compareAndSetState(0, acquires)) { // 比较并设置状态成功,状态0表示锁没有被占用// 设置当前线程独占setExclusiveOwnerThread(current); return true; // 成功}}else if (current == getExclusiveOwnerThread()) { // 当前线程拥有该锁int nextc = c + acquires; // 增加重入次数if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");// 设置状态setState(nextc); // 成功return true; }// 失败return false;}// 试图在共享模式下获取对象状态,此方法应该查询是否允许它在共享模式下获取对象状态,如果允许,则获取它protected final boolean tryRelease(int releases) {int c = getState() - releases;if (Thread.currentThread() != getExclusiveOwnerThread()) // 当前线程不为独占线程throw new IllegalMonitorStateException(); // 抛出异常// 释放标识boolean free = false; if (c == 0) {free = true;// 已经释放,清空独占setExclusiveOwnerThread(null); }// 设置标识setState(c); return free; }// 判断资源是否被当前线程占有protected final boolean isHeldExclusively() {// While we must in general read state before owner,// we don't need to do so to check if current thread is ownerreturn getExclusiveOwnerThread() == Thread.currentThread();}// 新生一个条件final ConditionObject newCondition() {return new ConditionObject();}// Methods relayed from outer class// 返回资源的占用线程final Thread getOwner() { return getState() == 0 ? null : getExclusiveOwnerThread();}// 返回状态final int getHoldCount() { return isHeldExclusively() ? getState() : 0;}// 资源是否被占用final boolean isLocked() { return getState() != 0;}/*** Reconstitutes the instance from a stream (that is, deserializes it).*/// 自定义反序列化逻辑private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {s.defaultReadObject();setState(0); // reset to unlocked state}
}
Sync类存在如下方法和作用如下。
3.2.2 NonfairSync类
NonfairSync类继承了Sync类,表示采用非公平策略获取锁,其实现了Sync类中抽象的lock方法,源码如下:
// 非公平锁
static final class NonfairSync extends Sync {// 版本号private static final long serialVersionUID = 7316153563782823691L;// 获得锁final void lock() {if (compareAndSetState(0, 1)) // 比较并设置状态成功,状态0表示锁没有被占用// 把当前线程设置独占了锁setExclusiveOwnerThread(Thread.currentThread());else // 锁已经被占用,或者set失败// 以独占模式获取对象,忽略中断acquire(1); }protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);}
}
说明: 从lock方法的源码可知,每一次都尝试获取锁,而并不会按照公平等待的原则进行等待,让等待时间最久的线程获得锁
。
3.2.3 FairSyn类
FairSync类也继承了Sync类,表示采用公平策略获取锁,其实现了Sync类中的抽象lock方法,源码如下:
// 公平锁
static final class FairSync extends Sync {// 版本序列化private static final long serialVersionUID = -3000897897090466540L;final void lock() {// 以独占模式获取对象,忽略中断acquire(1);}/*** Fair version of tryAcquire. Don't grant access unless* recursive call or no waiters or is first.*/// 尝试公平获取锁protected final boolean tryAcquire(int acquires) {// 获取当前线程final Thread current = Thread.currentThread();// 获取状态int c = getState();if (c == 0) { // 状态为0if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) { // 不存在已经等待更久的线程并且比较并且设置状态成功// 设置当前线程独占setExclusiveOwnerThread(current);return true;}}else if (current == getExclusiveOwnerThread()) { // 状态不为0,即资源已经被线程占据// 下一个状态int nextc = c + acquires;if (nextc < 0) // 超过了int的表示范围throw new Error("Maximum lock count exceeded");// 设置状态setState(nextc);return true;}return false;}
}
说明: 跟踪lock方法的源码可知,当资源空闲时,它总是会先判断sync队列(AbstractQueuedSynchronizer中的数据结构)是否有等待时间更长的线程,如果存在,则将该线程加入到等待队列的尾部,实现了公平获取原则。其中,FairSync类的lock的方法调用如下,只给出了主要的方法。
说明: 可以看出只要资源被其他线程占用,该线程就会添加到sync queue中的尾部,而不会先尝试获取资源。这也是和Nonfair最大的区别,Nonfair每一次都会尝试去获取资源,如果此时该资源恰好被释放,则会被当前线程获取,这就造成了不公平的现象,当获取不成功,再加入队列尾部。
3.3 类的属性
ReentrantLock类的sync非常重要,对ReentrantLock类的操作大部分都直接转化为对Sync和AbstractQueuedSynchronizer类的操作。
public class ReentrantLock implements Lock, java.io.Serializable {// 序列号private static final long serialVersionUID = 7373984872572414699L; // 同步队列private final Sync sync;
}
3.4 类的构造函数
ReentrantLock()型构造函数
默认是采用的非公平策略获取锁
public ReentrantLock() {// 默认非公平策略sync = new NonfairSync();
}
ReentrantLock(boolean)型构造函数
可以传递参数确定采用公平策略或者是非公平策略,参数为true表示公平策略,否则,采用非公平策略:
public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();
}
3.5 核心函数分析
通过分析ReentrantLock的源码,可知对其操作都转化为对Sync对象的操作,由于Sync继承了AQS,所以基本上都可以转化为对AQS的操作。如将ReentrantLock的lock函数转化为对Sync的lock函数的调用,而具体会根据采用的策略(如公平策略或者非公平策略)的不同而调用到Sync的不同子类。
所以可知,在ReentrantLock的背后,是AQS对其服务提供了支持,由于之前我们分析AQS的核心源码,遂不再累赘。下面还是通过例子来更进一步分析源码。 ¶
4.案例
4.1 公平锁
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;class MyThread extends Thread {private Lock lock;public MyThread(String name, Lock lock) {super(name);this.lock = lock;}public void run () {lock.lock();try {System.out.println(Thread.currentThread() + " running");try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}} finally {lock.unlock();}}
}public class AbstractQueuedSynchonizerDemo {public static void main(String[] args) throws InterruptedException {Lock lock = new ReentrantLock(true);MyThread t1 = new MyThread("t1", lock); MyThread t2 = new MyThread("t2", lock);MyThread t3 = new MyThread("t3", lock);t1.start();t2.start(); t3.start();}
}运行结果(某一次):
Thread[t1,5,main] running
Thread[t2,5,main] running
Thread[t3,5,main] running
说明: 该示例使用的是公平策略,由结果可知,可能会存在如下一种时序。
说明: 首先,t1线程的lock操作 -> t2线程的lock操作 -> t3线程的lock操作 -> t1线程的unlock操作 -> t2线程的unlock操作 -> t3线程的unlock操作。
根据这个时序图来进一步分析源码的工作流程。 t1线程执行lock.lock,下图给出了方法调用中的主要方法。
说明: 由调用流程可知,t1线程成功获取了资源,可以继续执行。
t2线程执行lock.lock,下图给出了方法调用中的主要方法。
说明: 由上图可知,最后的结果是t2线程会被禁止,因为调用了LockSupport.park。
t3线程执行lock.lock,下图给出了方法调用中的主要方法。
说明: 由上图可知,最后的结果是t3线程会被禁止,因为调用了LockSupport.park。
t1线程调用了lock.unlock,下图给出了方法调用中的主要方法。
说明: 如上图所示,最后,head的状态会变为0,t2线程会被unpark,即t2线程可以继续运行。此时t3线程还是被禁止。
t2获得cpu资源,继续运行,由于t2之前被park了,现在需要恢复之前的状态,下图给出了方法调用中的主要方法。
说明: 在setHead函数中会将head设置为之前head的下一个结点,并且将pre域与thread域都设置为null,在acquireQueued返回之前,sync queue就只有两个结点了。
t2执行lock.unlock,下图给出了方法调用中的主要方法。
说明: 由上图可知,最终unpark t3线程,让t3线程可以继续运行。
t3线程获取cpu资源,恢复之前的状态,继续运行。
说明: 最终达到的状态是sync queue中只剩下了一个结点,并且该节点除了状态为0外,其余均为null。
t3执行lock.unlock,下图给出了方法调用中的主要方法。
说明: 最后的状态和之前的状态是一样的,队列中有一个空节点,头节点为尾节点均指向它。
使用公平策略和Condition的情况可以参考上一篇关于AQS的源码示例分析部分,不再累赘。
【java】java ReentrantLock 源码详解相关推荐
- Java集合框架源码详解系列(一)
写在前面:大家好!我是晴空๓.如果博客中有不足或者的错误的地方欢迎在评论区或者私信我指正,感谢大家的不吝赐教.我的唯一博客更新地址是:https://ac-fun.blog.csdn.net/.非常 ...
- Java Thread类源码详解
概述 Java所有多线程的实现,均通过封装Thread类实现,所以深入Thread类,对深入理解java多线程很有必要 构造函数: Thread的构造函数,采用缺省的方式实现: //传入Runnabl ...
- java的数组与Arrays类源码详解
java的数组与Arrays类源码详解 java.util.Arrays 类是 JDK 提供的一个工具类,用来处理数组的各种方法,而且每个方法基本上都是静态方法,能直接通过类名Arrays调用. 类的 ...
- Java源码详解之NameValuePair
Java源码详解之NameValuePair NameValuePair仅仅是一个接口. 1. 类释义 /*** A name / value pair parameter used as an el ...
- java的String类源码详解
java的String类源码详解 类的定义 public final class Stringimplements java.io.Serializable, Comparable<String ...
- 【java】LinkedList1.8源码详解
目录 前言 概要 属性 构造方法 核心方法 get(int index) set(int index, E element) add(int index, E element) addAll(Coll ...
- 【JAVA秘籍心法篇-Spring】Spring XML解析源码详解
[JAVA秘籍心法篇-Spring]Spring XML解析源码详解 所谓天下武功,无坚不摧,唯快不破.但有又太极拳法以快制慢,以柔克刚.武功外式有拳打脚踢,刀剑棍棒,又有内功易筋经九阳神功.所有外功 ...
- java源码详解——String类
java源码详解--String类目录: Java String 类 下面开始介绍主要方法: Java charAt() 方法 Java compareTo() 方法 int compareTo(St ...
- (十三)Java工具类StringUtils中strip、stripStart、stripEnd剥离方法源码详解
1. strip方法源码解析 public static String strip(String str){return strip(str, null);} 源码解析:调用strip方法,参数是字符 ...
最新文章
- 镜像处理坐标 android,Android应用开发之Android重写ImageView实现图片镜像效果的代码教程...
- 3月14号,oracle的说道多多
- 计算机组装高考真题,年计算机组装与维修高考题.pdf
- TensorRT学习笔记(三)
- 把服务器文件备份到文件服务器,服务器之间文件备份方案、如何把服务器文件自动备份到另外一台服务器?...
- 课时 18-Kubernetes 调度和资源管理(子誉)
- Spring Boot文档地址
- flex 各组件对应的样式属性2
- 在J2SE应用程序中模拟CDI的会话和请求范围
- 【 Grey Hack 】加强版nmap
- 6.边缘检测:梯度——计算梯度Matlab实战_5
- Hibernate和MyBatis的缓存机制和比较
- 【Win 10 应用开发】在App所在的进程中执行后台任务
- 进程间通信方式有哪些?各自有哪些优缺点?
- 信息化知识中的重点:商业智能(BI)详解
- 利用C语言写一个等额本息的还款计算器。
- 检测站营销系列文章:机动车检测站如何做精益营销?
- iOS 上架流程图文详解2022版 (上)
- 赤喀高铁正式通车,日立电梯助力赤峰西站开通运营
- 小猪的Android入门之路 Day 8 part 3
热门文章
- 腾讯的抖音官号被封杀了?抖音回应:谣言
- 让IT人早点下班?高效的vPro平台帮你忙
- 今年别想了!iPhone 12系列没有120Hz高刷屏
- 有钱鹅!腾讯奖励万名员工每人一台16999元华为手机,员工“十动然鱼”
- 699美元起!一加8系列新机海外发布:120Hz 2K屏+骁龙865
- 中国移动咪咕公司:打造五新体验 做5G时代内容的聚合者与生产者
- “减压”成今年前十个月关键词:80后压力最大
- 中缅边境电信网络诈骗活动严重区域微信、支付宝等被封停
- 支付宝又“上新”!余额宝兄弟“余额佳”上线
- 突然!华为P30 Pro真机上手视频曝光:屏幕指纹解锁秒开