ReentrantLock 公平锁源码 第2篇
Reentrant 2
前两篇写完了后我自己研究了下,还有有很多疑惑和问题,这篇就继续以自问自答的方式写
如果没看过第1篇的可以先看看那个https://www.cnblogs.com/sunankang/p/16458795.html
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
进入acquireQueued
方法
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
//这个属性的作用是啥???
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
第一个问题
interrupted这个变量的作用
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
在parkAndCheckInterrupt
方法中最后return的是这个线程是否被打断,它的作用是啥?
先来回顾interrupt()
,interrupted()
和isInterrupted()
三者区别,长得很像,注意区分
interrupt()
的作用是中断线程,如果被中断的线程处于阻塞状态下,例如调用wait()
,join()
sleep()
,则抛出异常,否则只是设置一个中断标记为true,注意:仅仅是设置中断状态为true,并不会去 "中断" 线程
interrupted()
获取线程的中断状态并且清空中断状态(将中断状态设置为false)
isInterrupted()
获取线程的中断状态并不会清除中断状态
调用 interrupt 会使park方法立即结束,可以理解为唤醒
继续代码,看这个变量最后到了哪里
情况1 没有被打断过
假设线程没有被中断过,那么parkAndCheckInterrupt
返回就是false
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
那么不进入 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
这个if,获取到锁后返回false,回到acquire
方法
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
因为false,所以不进入selfInterrupt()
,方法结束
情况2 park或准备park,被唤醒后直接获取到了锁
先证明一下打断是会唤醒park中的线程的
我就再重复粘一下代码了,方便看
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
那么返回的就是true,回到上级acquireQueued
方法
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//返回到这里
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
因为返回true,所以进入if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
将interrupted返回true
假设循环获取到锁,那么再返回上一级acquire()
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
那么进入selfInterrupt()
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
是不是有点疑惑?我如果没有调用过interrupt()
那ReentrantLock就不做任何操作,我如果调用了,那它再给我调用一次 ???? 还有情况3
情况3 park或准备park,被唤醒后没有获取到锁
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//假设在调用shouldParkAfterFailedAcquire成功后,马上就要调用parkAndCheckInterrupt 时间片用完了
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
那么这个时候interrupted属性就有用了
首先要知道一点,一个被中断的线程是无法park的,除非清除了中断状态,即设置为将中断状态设置为false, 口说无凭,直接上图
第二张图还是在park状态,证明了被打断的线程是无法park的,除非将它中断状态设置为false
那么回到代码中就能知道这个的作用
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
如果线程被打断唤醒,还是在for(;;)
中,还是去获取锁,假设没有获取到呢?那么就一直在for循环中嘎嘎跑,因为线程的状态是被中断的,无法再次park了
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
那么现在懂了最后的Thread.interrupted()
作用了吗,就是将中断状态设置回false,好让线程没有获取到锁继续park
那这时候可能就问了:那你ReentrantLock把中断状态给我清空了,我自己如果有需要根据中断状态来判断的代码咋办啊?
好,咱们从park先被打断来捋一下
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
因为被打断,线程醒来,执行Thread.interrupted()
并清空中断状态,返回true
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
//进入这里
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
因为返回的是true,所以进入if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
的代码块,将interrupted
属性设置为true
那么for(;;)
循环再来一次,如果没有获取到锁.继续park,直到被唤醒,走tryAcquire()
获取到为止,那么此时interrupted
变量就为true了
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
那么退出acquireQueued()
方法回到acquire()
中,因为acquireQueued()
返回的是true,所以进入selfInterrupt()
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
所以懂了吗?
ReentrantLock 公平锁源码 第2篇的更多相关文章
- ReentrantLock 公平锁源码 第0篇
ReentrantLock 0 关于ReentrantLock的文章其实写过的,但当时写的感觉不是太好,就给删了,那为啥又要再写一遍呢 最近闲着没事想自己写个锁,然后整了几天出来后不是跑丢线程就是和没 ...
- ReentrantLock 公平锁源码 第1篇
ReentrantLock 1 这篇还是接着ReentrantLock的公平锁,没看过第0篇的可以先去看上一篇https://www.cnblogs.com/sunankang/p/16456342. ...
- ReentrantLock之非公平锁源码分析
本文分析的ReentrantLock所对应的Java版本为JDK8. 在阅读本文前,读者应该知道什么是CAS.自旋. 由于ReentrantLock的公平锁和非公平锁中有许多共同代码,本文只会对这两种 ...
- ReentrantLock 的公平锁源码分析
ReentrantLock 源码分析 以公平锁源码解析为例: 1:数据结构: 维护Sync 对象的引用: private final Sync sync; Sync对象继承 AQS, Syn ...
- ReentrantLock之公平锁源码分析
本文分析的ReentrantLock所对应的Java版本为JDK8. 在阅读本文前,读者应该知道什么是CAS.自旋. 本文大纲 1.ReentrantLock公平锁简介 2.AQS 3.lock方法 ...
- ReentrantLock与synchronized 源码解析
一.概念及执行原理 在 JDK 1.5 之前共享对象的协调机制只有 synchronized 和 volatile,在 JDK 1.5 中增加了新的机制 ReentrantLock,该机制的诞生并 ...
- Java关于ReentrantLock获取锁和释放锁源码跟踪
通过对ReentrantLock获取锁和释放锁源码跟踪主要想进一步深入学习AQS. 备注:AQS中的waitStatus状态码含义:
- ReentrantLock锁 源码分析
根据下面代码分析下ReentrantLock 获得锁和释放锁的过程 ReentrantLock lock = new ReentrantLock(); lock.lock();//获得锁 lock.u ...
- ReentrantLock 与 AQS 源码分析
ReentrantLock 与 AQS 源码分析 1. 基本结构 重入锁 ReetrantLock,JDK 1.5新增的类,作用与synchronized关键字相当,但比synchronized ...
随机推荐
- XCTF练习题---MISC---就在其中
XCTF练习题---MISC---就在其中 flag:flag{haPPy_Use_0penSsI} 解题步骤: 1.观察题目,下载附件 2.拿到手以后发现是一个数据包格式,直接上Wireshark查 ...
- 「BUAA OO Unit 2 HW8」第二单元总结
「BUAA OO Unit 2 HW8」第二单元总结 目录 「BUAA OO Unit 2 HW8」第二单元总结 Part 0 前言 Part 1 第五次作业 1.1 作业要求 1.2 架构设计 1. ...
- 1903021121—刘明伟—Java第三周作业—学习在eclipse上创建并运行java程序
项目 内容 课程班级博客链接 19信计班(本) 作业要求链接 第三周作业 作业要求 每道题要有题目,代码,截图 扩展阅读 eclipse如何创建java程序 java语言基础(上) 扩展阅读心得: 想 ...
- SpringBoot线程池
1.遇到的场景 提高一下插入表的性能优化,两张表,先插旧的表,紧接着插新的表,若是一万多条数据就有点慢了 2.使用步骤 用Spring提供的对ThreadPoolExecutor封装的线程池Threa ...
- 896.Montonic Array - LeetCode
Question 896. Monotonic Array Solution 题目大意: 类似于数学中的减函数,增函数和物理中的加速度为正或为负 思路: 先比较前两个是大于0还是小于0,如果等于0就比 ...
- 好客租房54-props深入(children属性)
children属性 表示组件标签的子节点 当组件标签有子节点是 props就会有该属性 props中的组件可以是任意属性 都是可以渲染的 //导入react import React fro ...
- 安装Sonarqube到CentOS(YUM)
SonarQube 是一个用于代码质量管理的开源平台,用于管理源代码的质量. 通过插件形式,可以支持包括 java, C#, C/C++, PL/SQL, Cobol, JavaScrip, Groo ...
- Golang可重入锁的实现
Golang可重入锁的实现 项目中遇到了可重入锁的需求和实现,具体记录下. 什么是可重入锁 我们平时说的分布式锁,一般指的是在不同服务器上的多个线程中,只有一个线程能抢到一个锁,从而执行一个任务.而我 ...
- 《Effective C++》阅读总结(三):资源管理
C++中的资源管理非常重要,在将资源加载到内存后,便可以使用资源了,当我们不再需要资源时,我们要保证其正确的释放,才能将其占用的内存空间归还给操作系统,不正确的释放很容易造成内存泄漏.本章以资源管理类 ...
- git 无法拉取最新代码
删除本地文件后,想从远程仓库中重新新Pull最新代码,但是执行了git pull origin develop 命令后始终无法拉取下来 提示 Already up-to-date. 原因:当前本地库处 ...