首页 > 编程学习 > 【并发编程】 --- Reentrantlock源码解析3:公平锁释放锁过程超详细解析

文章目录

  • 1 公平锁释放锁的过程
    • 1.1 从源码中看公平锁释放锁的过程
    • 1.2 以源码为基础总结出的公平锁释放锁的流程
  • 2 需要思考的问题
    • 2.1 问题1 --- 释放锁的过程中有可能导致尾节点的ws为-1么 ---> 不可能
    • 2.2 问题2 --- 公平锁加锁和解锁的过程还有值得探究的地方么? ---> 当然有!!!
    • 2.3 问题3 --- 简单对比一下Reentrantlock的自旋和synchronized的自旋
  • 3 背后小故事

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


所需前置知识:
《【并发编程】 — Reentrantlock源码解析2:公平锁加锁过程超详细解析》
《【并发编程】 — Reentrantlock源码解析1:同步方法交替执行的处理逻辑》


1 公平锁释放锁的过程


1.1 从源码中看公平锁释放锁的过程

假设线程t1持有锁,线程t2、t3被park了 ,t2、t3所组成的Node链表结构如下所示:
在这里插入图片描述


接下来我们来看一下线程t1释放锁并唤起线程t2的过程:
在这里插入图片描述
上面蓝色序号标的1、2、3描述的过程为 : 线程t1

  • (1)尝试释放锁
  • (2)唤醒排在head后第一个被park且没被cancel掉的线程 ;

红色的序号 + 中间的Node链表 描述的过程为 : 排在head后第一个被park且没被cancel掉的线程

  • (1)获取到锁
  • (2)从Node链表中出队
  • (3)执行同步方法 — 这个可以想象,哈哈

1.2 以源码为基础总结出的公平锁释放锁的流程

注意: 我这里就不考虑重入锁了。

具体流程如下:

在这里插入图片描述


2 需要思考的问题


2.1 问题1 — 释放锁的过程中有可能导致尾节点的ws为-1么 —> 不可能

在1.1中我假设的是Node链表中有两个线程被park了,所以最终t1释放锁后,Node链表成了下面的样子:
在这里插入图片描述
那假若Node链表中只有一个线程(权当认为是t2)被park了,那有没有可能t1释放锁后,Node链表变成下面的样子呢? — 不可能!!!
在这里插入图片描述
答案是不可能 —> 因为如果只有线程t2在Node链表中,则它的waitStatus就不可能被改为-1 ,因为该值被改为-1的情况只有一种,就是在线程t2所在的Node后面又来了一下线程t3,由t3去改这个值!!!

因此这就应了我在上文《【并发编程】 — Reentrantlock源码解析2:公平锁加锁过程超详细解析》倒数第1和倒数第2个图里所说的一个事 : 在并发情况下对于一个新加入Node链表的线程来说,其前面Node的waitStatus只可能有两种值:1(线程被cancel的情况)和 0。 —》 这其实保证了:

  • (1)每一个线程在加入Node链表后都至少会进行一次自旋!!!
  • (2)同时也维护了整个加锁和解锁过程的统一!!!

很牛逼!!!


2.2 问题2 — 公平锁加锁和解锁的过程还有值得探究的地方么? —> 当然有!!!

换句话说,除了我下面两篇文章和本篇文章所讲的内容,还有什么值得在Reentrantlock的公平锁里需要探究的内容呢???
《【并发编程】 — Reentrantlock源码解析2:公平锁加锁过程超详细解析》
《【并发编程】 — Reentrantlock源码解析1:同步方法交替执行的处理逻辑》

当然或许需要探究的还有很多,但在我看来尤其值得探究的是:

当一个新的线程过来如果发现锁没被占用,那对于公平锁来说,它肯定不能直接去把锁抢占了(否则对于已经在排队的线程来说”公平何在呢???“ )而是需要先去看看它自己是否需要排队 —》 在高并发的情况下,该线程到底怎样判断它是否真的需要排队或者不需要排队呢???

Doug Lea大神对此过程的实现代码具体如下。
可以看到代码寥寥数行,但注释却是一大坨!!!
这仅仅几行的代码是如何考虑尽所有的情况的??? —》 限于篇幅,下篇文章进行揭秘!!!
在这里插入图片描述
在此很想事先感叹一声,真是牛X,就这么几行代码却考虑尽了所有情况!!!


2.3 问题3 — 简单对比一下Reentrantlock的自旋和synchronized的自旋

如果不考虑线程被Cancel的情况,对于Reentrantlock(公平锁和非公平锁一样,有兴趣的可以撸源码)来说,每个线程至少会在acquireQueued方法中的死循环里跑两趟,也就是说至少会自旋两次 —》 如果考虑线程被Cancel的情况下,可能会跑的次数更多。

通过文章《【并发编程】 — synchronized锁的升级过程 + JDK1.6对synchronized关键字的其他优化简介》可以知道synchronized关键字在JDK1.6之后默认使用了自适应性自旋,且默认的自旋次数为10次,而JDK原作者通过研究认为,自旋次数在20-100之间看起来会更好!!!

—> 我想这里可能是有些资料上说JDK1.6之后synchronized关键字的效率比Reentrantlock更高的一个原因。


3 背后小故事

昨天(2020-04-11)晚上1点多基本看明白了具体的原理,兴奋到夜里3点多才睡着,今天(2020-04-11)早上不到6点就爬起来把其总结了出来 —> Doug Lea确实真是厉害!!!


end !!!

Copyright © 2010-2022 ngui.cc 版权所有 |关于我们| 联系方式| 豫B2-20100000