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篇的更多相关文章

  1. ReentrantLock 公平锁源码 第0篇

    ReentrantLock 0 关于ReentrantLock的文章其实写过的,但当时写的感觉不是太好,就给删了,那为啥又要再写一遍呢 最近闲着没事想自己写个锁,然后整了几天出来后不是跑丢线程就是和没 ...

  2. ReentrantLock 公平锁源码 第1篇

    ReentrantLock 1 这篇还是接着ReentrantLock的公平锁,没看过第0篇的可以先去看上一篇https://www.cnblogs.com/sunankang/p/16456342. ...

  3. ReentrantLock之非公平锁源码分析

    本文分析的ReentrantLock所对应的Java版本为JDK8. 在阅读本文前,读者应该知道什么是CAS.自旋. 由于ReentrantLock的公平锁和非公平锁中有许多共同代码,本文只会对这两种 ...

  4. ReentrantLock 的公平锁源码分析

    ReentrantLock 源码分析   以公平锁源码解析为例: 1:数据结构: 维护Sync 对象的引用:   private final Sync sync; Sync对象继承 AQS,  Syn ...

  5. ReentrantLock之公平锁源码分析

    本文分析的ReentrantLock所对应的Java版本为JDK8. 在阅读本文前,读者应该知道什么是CAS.自旋. 本文大纲 1.ReentrantLock公平锁简介 2.AQS 3.lock方法 ...

  6. ReentrantLock与synchronized 源码解析

    一.概念及执行原理   在 JDK 1.5 之前共享对象的协调机制只有 synchronized 和 volatile,在 JDK 1.5 中增加了新的机制 ReentrantLock,该机制的诞生并 ...

  7. Java关于ReentrantLock获取锁和释放锁源码跟踪

    通过对ReentrantLock获取锁和释放锁源码跟踪主要想进一步深入学习AQS. 备注:AQS中的waitStatus状态码含义:

  8. ReentrantLock锁 源码分析

    根据下面代码分析下ReentrantLock 获得锁和释放锁的过程 ReentrantLock lock = new ReentrantLock(); lock.lock();//获得锁 lock.u ...

  9. ReentrantLock 与 AQS 源码分析

    ReentrantLock 与 AQS 源码分析 1. 基本结构    重入锁 ReetrantLock,JDK 1.5新增的类,作用与synchronized关键字相当,但比synchronized ...

随机推荐

  1. 聊聊redis的主从复制吧

    聊聊基础概念 主从复制与主从替换 主从复制不同于主从替换,主从复制是正常情况下主节点同步数据到从节点:主从替换是主节点挂了之后,把从节点替换为主节点: 从节点存在的意义:备份主节点数据+负载均衡(对外 ...

  2. ChCore Lab3 用户进程和异常处理 实验笔记

    本文为上海交大 ipads 研究所陈海波老师等人所著的<现代操作系统:原理与实现>的课程实验(LAB)的学习笔记的第三篇:用户进程与异常处理.所有章节的笔记可在此处查看:chcore | ...

  3. QMetaObject::connectSlotsByName: No matching signal for XXX 原理探究

    问题引出: 在尝试实现<Qt5.9 c++开发指南>混合UI编程章节时,用纯代码形式实现了个小按钮,然后加了个对应的槽函数,运行时就提示了这个信息. 原因探究: 首先查阅官方手册中的说明: ...

  4. IDEA新建项目时的默认配置与模版配置

    今天一大早,群里(点击加群)有小伙伴问了这样的一个问题: 在我们使用IDEA开发项目的时候,通常都会有很多配置项需要去设置,比如对于Java项目来说,一般就包含:JDK配置.Maven配置等.那么如果 ...

  5. Vue问题

    vue问题 #(1)vouter的addRoutes方法---用户权限 //自定义添加路由方法,防止重复添加路由 #使用后 路由结构 const user = () => import('../ ...

  6. JavaSE_关键字 接口 代码块 枚举

    1 Java中的关键字 1.1 static关键字 static特点 : 静态成员被所在类的所有对象共享 随着类的加载而加载 , 优先于对象存在 可以通过对象调用 , 也可以通过类名调用 , 建议使用 ...

  7. 在项目中如何直接使用hystrix?

    一.背景 最近由于一些背景原因,需要在项目中需要对接口进行限流.所以就考虑到了直接使用Hystrix.但是呢,又不想直接使用SpringCloud,而是直接引入原生,现在发现挺好用的,所以记录下来,分 ...

  8. 手把手教你 Docker搭建mysql并配置远程访问

    一.使用docker部署mysql 1.在docker中搜索要安装的mysql docker search mysql (这步其实可以跳过O(∩_∩)O哈哈~) 2.拉取mysql镜像 docker ...

  9. k8s中label和label selector的基本概念及使用方法

    1.概述 在k8s中,有一个非常核心的概念,就是label(标签),以及对label的使用,label selector. 本文档中,我们就来看看:1.什么是标签,2.如何定义标签,3.什么是标签选择 ...

  10. 【Github】 Github访问不是私密连接问题

    前言 GitHub是一个软件项目的托管平台,是我们经常需要访问的,我原本在学校时候虽然网速比较慢,但是还以能够满足一些代码下载和上传的,在暑假回到家,再去访问的时候就出现了不能访问的问题. 问题描述 ...