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中的线程的

 ReentrantLock 公平锁源码 第2篇

我就再重复粘一下代码了,方便看

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, 口说无凭,直接上图

 ReentrantLock 公平锁源码 第2篇

 ReentrantLock 公平锁源码 第2篇

第二张图还是在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();
}

所以懂了吗?

Original: https://www.cnblogs.com/sunankang/p/16461261.html
Author: Jame!
Title: ReentrantLock 公平锁源码 第2篇

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/574912/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • springcloud-@RefreshScope注解

    一.问题 注入过后不调用方法获取不到对应的value值 二.演示 @Component @RefreshScope public class Val { @Value("…

    Java 2023年6月16日
    081
  • docker nginx1.7.6+keepalived实现双机热备

    0.前提条件 环境两台ubuntu版本14.04 64位系统(并获取root权限) 假设两台服务器ip为:172.16.34.214(master),172.16.34.215(b…

    Java 2023年6月9日
    079
  • 最简单的单线程变多线程的例子

    最简单的单线程变多线程的例子 背景 不知道你项目里有么有这样一个函数,这个函数里调用了大概十几来个函数,这十几个函数依次的从头写到位,而且这几个函数都是相对独立的,谁先执行谁后执行…

    Java 2023年6月8日
    083
  • git操作合集与知识点

    git合作开发须知 我们在自己的私人分支开发好各自的模块后,就要合并到master,这个时候在idea里边先切换到master, 然后update一下获取最新更新,解决一下冲突,最…

    Java 2023年6月5日
    084
  • C语言-字符串函数的实现(三)之strcat

    C语言中的字符串函数有如下这些 获取字符串长度 strlen 长度不受限制的字符串函数 strcpy strcat strcmp 长度受限制的字符串函数 strncpy strnc…

    Java 2023年6月10日
    088
  • RestFul API 统一格式返回 + 全局异常处理

    一、背景 在分布式、微服务盛行的今天,绝大部分项目都采用的微服务框架,前后端分离方式。前端和后端进行交互,前端按照约定请求 URL路径,并传入相关参数,后端服务器接收请求,进行业务…

    Java 2023年6月5日
    098
  • 技能篇:linux服务性能问题排查及jvm调优思路

    只要业务逻辑代码写正确,处理好业务状态在多线程的并发问题,很少会有调优方面的需求。最多就是在性能监控平台发现某些接口的调用耗时偏高,然后再发现某一SQL或第三方接口执行超时之类的。…

    Java 2023年6月5日
    092
  • mapreduce运行问题 :java.io.EOFException

    写MapReduce Dome 案例时i,报出 java.io.EOFException java;gutter:true;java.lang.Exception: java.io…

    Java 2023年6月9日
    091
  • xampp相关配置文件所在路径

    环境: Xampp3.2.4一般来讲,可以在xampp的控制面板打开先关的配置文件,但是出现有些问题的时候,还是需要到别的路径下找到相关配置文件修改才行。 todo:更改xampp…

    Java 2023年6月7日
    087
  • 管理敏感词+图片识别文字审核敏感词

    1.DFA实现原理 DFA全称为:Deterministic Finite Automaton,即确定有穷自动机。 存储:一次性的把所有的敏感词存储到了多个map中,就是下图表示这…

    Java 2023年6月9日
    092
  • 数据库CRUD封装

    数据库CRUD封装 数据库CRUD封装 在一个项目中,我们会进行后台数据库的连接。所以会频繁的使用数据库的CRUD操作,所以我们能不能抽取公共部分,并形成一个工具类呢?我们来试试。…

    Java 2023年6月5日
    0115
  • Spring MVC源码——Servlet WebApplicationContext

    上一篇笔记(Spring MVC源码——Root WebApplicationContext)中记录了下 Root WebApplicationContext 的初始化代码.这一篇…

    Java 2023年5月30日
    088
  • MCU软件最佳实践——矩阵键盘驱动

    1.矩阵键盘vs独立按键 在mcu应用开发过程中,独立按键比较常见,但是在需要的按键数比较多时,使用矩阵键盘则可以减少io占用,提高系统资源利用率。例如,某mcu项目要求有16个按…

    Java 2023年6月6日
    067
  • II-第十二章:(1)SpringCloud Alibaba简介

    一、SpringCloud Alibaba Spring Cloud Netflix Projects Entering Maintenance Mode 什么是维护模式? 将模块…

    Java 2023年5月29日
    061
  • Android JNI中C调用Java方法

    背景需求 我们需要在JNI的C代码调用Java代码。实现原理:使用JNI提供的反射借口来反射得到Java方法,进行调用。 JNI关键方法讲解。 在同一个类中,调用其他方法 JNIE…

    Java 2023年5月29日
    083
  • nginx模块

    博客园 :当前访问的博文已被密码保护 请输入阅读密码: Original: https://www.cnblogs.com/chrdai/p/11330763.htmlAuthor…

    Java 2023年5月30日
    098
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球