

ReentrantLock 公平锁源码 第2篇 - Jame!
source link: https://www.cnblogs.com/sunankang/p/16461261.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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(); }
所以懂了吗?
__EOF__
Recommend
-
25
背景 2020年风云变幻,原本以为嵌入式端侧 AI 芯片“大菊已定”。 但国内最大智能电视芯片厂商——晶晨半导体(Amlogic...
-
14
深圳某小公司面试题:AQS是什么?公平锁和非公平锁?ReentrantLock?
-
20
鸠摩3天前本文将详细介绍在Ubuntu16.04 LTS上对OpenJDK8进行编译,为了方便大家快速搭建起OpenJDK8的调试开发环境,我还录制了对应的视频放到了B站上,大家可...
-
4
第2篇-Java虚拟机这样来调用Java主类的main()方法 | HeapDump性能社区文章>第2篇-Java虚拟机这样来调用Java主类的main()方法第2篇-Java虚拟机这样来调用Java主类的main()方法
-
5
2022 Global Game Jame 深圳站报名开启 发表于13小时前 评论0
-
5
Go Quiz: 从Go面试题看defer的注意事项第2篇发布于 今天 13:58 这是Go Quiz系列的第5篇,是考察Go语言的defer语义,也是defer语义的第2...
-
8
在这篇文章中,我们来重点关注Scratch静态资源相关的API, 了解这些资源是如何被存储以及如何被加载的。 关注的静态资源包括: Scratch project 中的静态资源 项目缩略图 Scratch pr...
-
3
mysql拆分字符串作为查询条件 有个群友问一个问题
-
7
ReentrantLock 1 这篇还是接着ReentrantLock的公平锁,没看过第0篇的可以先去看上一篇https://www.cnblogs.com/sunankang/p/16456342.html 这篇就以问题为导向,先提出问题,然后根据问题去看代码
-
6
图解ReentrantLock底层公平锁和非公平锁实现原理
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK