【并发编程】 --- Reentrantlock源码解析5:再探不可中断性 + 线程unpark后诡异的Thread.interrupted()判断

el/2023/10/1 3:24:06

文章目录

  • 1 想要读懂这篇文章必须要拥有的前置知识
  • 2 想写这篇文章的原因
  • 3 困扰我很久的Reentrantlock源代码1 --- 貌似无用的变量failed
  • 4 困扰我很久的Reentrantlock源代码2 --- unpark后为啥要来个Thread.interrupted();
  • 5 小记

源码地址:https://github.com/nieandsun/concurrent-study.git


1 想要读懂这篇文章必须要拥有的前置知识

《【并发编程】 — synchronized/ReentrantLock两大特性(可重入性和不可中断性)介绍》
《【并发编程】— interrupt、interrupted和isInterrupted使用详解》
《【并发编程】 — Reentrantlock源码解析2:公平锁加锁过程超详细解析》
《【并发编程】 — Reentrantlock源码解析3:公平锁释放锁过程超详细解析》


2 想写这篇文章的原因

下面这幅图是我在文章《【并发编程】 — Reentrantlock源码解析3:公平锁释放锁过程超详细解析》中给出的一幅图。
在这里插入图片描述
不知道大家对这幅图中的代码有没有疑惑??? —> 说实话我有,而且困扰了我很久很久!!!
本篇文章将主要针对我困扰的问题进行讨论,并给出我自认为可以自圆其说的解释!!!


3 困扰我很久的Reentrantlock源代码1 — 貌似无用的变量failed

我们先来看一下1中红色标号为5的代码。

我的疑问如下:
在这里插入图片描述
可能有人会说:

在try那块代码里 failed = false;这句代码之前有可能会抛出异常,如果抛了异常之后就会走finally代码块,那个时候failed还是初始值true ,那这个逻辑就有意义了。 —> 这句话说的一点没错,但是这是一种怎样的情况呢???

假如你恰巧看过这篇文章《【并发编程】 — synchronized/ReentrantLock两大特性(可重入性和不可中断性)介绍》,那我觉得你应该是可以回答上来的:

使用Reentrantlock锁,当某个线程正在被park时,使用stop方法中断此线程,就正好是这种情况。


相信大家都知道使用stop方法中断某个线程,会直接释放掉本线程所持有的所有资源,举个简单的栗子来说,假如我们正在使用某个线程下载电影,如果该线程通过stop进行中断,则原来下载的内容将全部丢失,因此可以简单将其理解为“暴力中断”(自己发明的词,见笑☺),只要调用了,就不可能恢复了 —》 应该正是基于这个原因,JDK官方将其@Deprecated了。

但是万一有些业务确实就需要这种“暴力中断”呢???

看过 《【并发编程】 — synchronized/ReentrantLock两大特性(可重入性和不可中断性)介绍》这篇文章的应该知道,

synchronized关键字的处理逻辑是,假如线程被park了,即使使用stop进行“暴力中断”, 对不起, 没法给你立即中断 —》 但是当你有了抢锁资格后,就立即给你中断!!!

仔细想想这个逻辑 ,是不是一个挺操蛋的逻辑??? 我都采用暴力中断了,你还不给我立即中断,而当我有抢锁资格了,我肯定这时候就大概率不想中断了啊,你到立刻给我来个暴力中断!!!

或许Doug Lea大神也觉得这个逻辑比较操蛋吧。。。所以人家在Reentrantlock中做了改进!!!

要很详整的说出来具体的改进应该还要把cancelAcquire的源码给搬出来,但是这里我不打算搬出来了,所以只是简单的,从比较宏观的角度去阐述一下(当然私底下,我已经打过n遍断点验证过我的观点了,有兴趣的可以自己亲自动手试试!!!)。


假设Node链表中有t2和t3在排队等着抢锁,其结构如下,这时候突然想用stop()方法“暴力中断”t2线程。
在这里插入图片描述

在不看源码的情况下,我觉得你仔细想想应该也能想出个一二来。 首先我们应该知道既然t2要被“暴力中断”了,那这个线程说的直白点就直接相当于没有了。。。 那该线程在Node链表里肯定也就没必要存在了吧; —》 反过来想,假若我不处理Node链表,在解锁时发现Node链表里竟然除了head外还有一个线程为null的Node,你是不是感觉也很不可理解??? —> 想到这里其实你就知道Doug Lea大神会干什么了 —》 没错,将被stop的线程从Node链表里出队。

以上图而言Doug Lea大神的逻辑基本如下:

  • (1)既然stop了,线程会被暴力中断,但是因为有finally关键字,所以会进入到cancelAcquire()方法
  • (2)将线程t2对应的节点的waitStatus置为1
  • (3)unpark线程t3 ,让线程t3去自旋 —》 由文章《【并发编程】 — Reentrantlock源码解析2:公平锁加锁过程超详细解析》可知,由于线程t2所在Node的waitStatus此时为1,因此会在t3自旋时,将其从Node链表中给踢出去 —》 由此便完成了被stop的线程t2所在节点的出队。

4 困扰我很久的Reentrantlock源代码2 — unpark后为啥要来个Thread.interrupted();

这块代码真是困扰了很久很久很久!!!! - > 甚至都有时候都怀疑自己太钻死牛角尖了!!!

昨天非常有幸,在B站看到了子路老师的讲解,后又思考良久,甚至找子路老师聊了该问题,终于找到了一个可以自圆其说的原因!!!

在这里插入图片描述
如果看懂了我这篇文章《【并发编程】— interrupt、interrupted和isInterrupted使用详解》再看上面这张图,你脑子要TMD崩溃!!!

为啥这样说呢???

我们这里直接看线程t被打断的情况,他做了个啥啊,这是!!! —》 先判断一下是否被打断(这里要注意一下interrupted和isInterrupted的区别,但是对于这里不重要!!!) —》如果是 —》我再打断一次

  • 而我想大家应该都知道使用interrupt做打断,其实就相当于皇帝在某个妃子房间里玩耍,突然某个太监在外面喊:万岁,注意龙体,该去休息了。---》 但是皇帝到底停不停,全由它自己说了算!!!
  • 那TMD就让人疑惑了,Doug Lea这是要干啥啊,闲的蛋疼?在中间传个话???
  • 如果它啥事不做,不也是这样么???—》 也就是说上面的流程不是和下面的流程一个样么???— 而且下面是不是效率还更高一些???

在这里插入图片描述
那Doug Lea为啥要这样搞呢???


研究了n天后,结合子路老师的解答,我给出的答案是为了代码格式的统一,以及某些锁少写几行重复代码!!!

首先来看一下用到parkAndCheckInterrupt() 这个方法的所有方法:

  • acquireQueued方法 — 独占锁自旋主要逻辑
  • doAcquireInterruptibly方法 — 可打断的独占锁自旋主要逻辑
  • doAcquireShared方法 — 共享锁自旋主要逻辑
  • doAcquireSharedInterruptibly方法— 可打断的共享锁自旋主要逻辑

这算不算格式上的统一??? —》 有兴趣的可以留言交流!!!


其次来简单看一下acquireQueueddoAcquireInterruptibly两个方法的简单比较:
在这里插入图片描述

这算不算在某些锁少写几行重复代码 —》 有兴趣的可以留言交流!!!


5 小记

有时候感觉自己像个傻子!!! 总习惯于沿着一个方向不停的钻钻钻!!!
上学时老师说我适合搞研究,能做事, 但又不适合,做的太多,研究的太深, 但是却总不发文章。
于是思考了将近半年,下定决心要去工作,所幸手头的工作都比较简单 。

因此想快速搭建起自己的知识体系,但是发现自己还是会时不时的钻钻钻!!!—》于是学的东西慢且少,而且越学越觉得不懂的越多!!! —》 所以¥¥¥ 。。。

唉!!!唉!!!唉!!!


end!!!


http://www.ngui.cc/el/1113656.html

相关文章

【并发编程JVM】--- 强软弱虚四种引用 + ThreadLocal内存泄漏原因分析

本篇文章整理自马士兵老师的公开课(哔哩哔哩) 源码地址:https://github.com/nieandsun/concurrent-study.git 文章目录【1】强软弱虚四种引用【1.1】强引用【1.2】软引用【1.3】弱引用【1.4】虚引用【2】ThreadLocal使用方式 底层原理分析【…

【并发编程】 --- Lock/Condition完成生产者消费者模式+ABCABC顺序打印问题

源码地址:https://github.com/nieandsun/concurrent-study.git 文章目录1 生产者消费者问题2 ABCABC。。。三个线程顺序打印问题2.1 基本不费脑子的实现方式 --- 且比较容易感受到定点通知的含义2.2 比较灵活的方式1 生产者消费者问题 使用一个Condition极其类似于w…

【并发编程】 --- 从五个维度对比synchronized关键字和Lock

文章目录维度1 --- 从原始构成上来说维度2 --- 从使用方法上来说维度3 --- 从等待是否可中断上来说维度4 --- 从加锁是否公平角度来说维度5 --- 从线程间的通信来说维度1 — 从原始构成上来说 synchronized是关键字,属于JVM层面Lock是具体类(java.util.concurrent.…

【并发编程】--- 阻塞队列(BlockingQueue)简介

源码地址:https://github.com/nieandsun/concurrent-study.git 文章目录1 阻塞队列的含义2 为什么用? 有什么好处?3 常用的BlockingQueue3.1 ArrayBlockingQueue简介3.2 LinkedBlockingQueue简介3.3 PriorityBlockingQueue简介3.4 DelayQueue…

【并发编程】--- 线程池七大参数+四种拒绝策略 + 如何合理配置线程数等简介

源码地址:https://github.com/nieandsun/concurrent-study.git 文章目录1 线程池七大参数2 RejectedExecutionHandler--- 四种拒绝策略(官方提供)3 threadFactory --- 线程工厂相关的注意事项4 如何自己new一个线程池 --- 简单结合了一下我们…

【消息中间件】--- RocketMQ核心概念介绍 + 生产者简介 + 消息存储简介

本文对应源码地址:https://github.com/nieandsun/rocketmq-study rocketmq官网:https://rocketmq.apache.org/docs/quick-start/ rocketmq github托管地址(这里直接给出的是中文docs地址):https://github.com/apache/r…

【消息中间件】--- RocketMQ消费者简介(集群、广播消费,推模式,拉模式)

本文对应源码地址:https://github.com/nieandsun/rocketmq-study rocketmq官网:https://rocketmq.apache.org/docs/quick-start/ rocketmq github托管地址(这里直接给出的是中文docs地址):https://github.com/apache/r…

【redis知识点整理】---centos7下redis的安装 、启动、连接和关闭

最近遇到一个线上bug,关系到了fastjson、dubbo、 redis 和 泛型,之所以这样说,是因为排查问题 到 解决问题的思路大致如此 —> 我觉得我之后应该会抽出时间写一篇博客,聊聊这个事 —> 但是最近实在太忙了,很多想…

【redis知识点整理】 --- Redis的持久化

redis 支持 RDB 和 AOF 两种持久化机制, 持久化可以避免因进程退出而造成数据丢失。 文章目录1 RDB持久化 --- redis默认开启的持久化方式1.1 RDB持久化机制 --- 手动触发1.2 RDB持久化机制 --- 自动触发1.3 bgsave运行流程 --- RDB主流触发方式1.4 RDB恢复方式 持…

【redis知识点整理】 --- springboot2.X使用lettuce连接池集成redis

本文代码对应的github地址:https://github.com/nieandsun/redis-study 不出意外的话,很多公司操作redis应该还是使用的jedis。 当然应该也有公司比较能耐,直接把操作redis的所有方法进一步封装成API或Utils类,然后打成jar包&#…