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

源码地址: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();

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

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

为啥这样说呢???

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

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

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


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

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

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

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


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

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


5 小记

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

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

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


end!!!

发布了231 篇原创文章 · 获赞 336 · 访问量 54万+

猜你喜欢

转载自blog.csdn.net/nrsc272420199/article/details/105466105