前面一篇文章《【并发编程】 — Reentrantlock源码解析1:同步方法交替执行的处理逻辑》讲过无论是synchronized还是Lock锁,让某块代码变为同步的本质
就是: 当一个线程执行该方法后,其他线程无法进入该方法,对应于Reentrantlock来说就是让其他线程的lock()方法无法正常返回。
那篇文章里讲过在方法交替执行时,Reentrantlock公平锁的主要逻辑如下,这里就不再过多叙述。
线程2的入队代码就这么一点,但是过程挺有意思的,这里总结如下:
当然这只是一种情况,即线程t2 成功排在了队列第2的位置。
其实应该还有一种情况: 即线程t2完成了上图中粉色字体的第(2)步,这时线程t3刚好进入addWaiter()方法,发现tail已经不为null了,这时候有可能t3会先排在队列第2的位置
,这里就不画图了。。。
当然还有可能刚开始时一下有多个线程进入enq方法的情况,这里就不展开了。。。
—> 此时应该可以引申出一个思考: Reentrantlock所谓的公平锁,到底公平在哪???
—> 从这里我们可以看出来,这里所谓的公平并不是你先尝试获取锁,你就一定会最先获取到锁,而是你最先进入到了Node队列,你最先获取到锁!!! —这一点我感觉也是非常非常重要。
要非常注意三点(即我在图中用红色字体写的三句话):
本文后面的内容都假设t2的Node排在了队列第2的位置。
其实这两点应该可以很容易的想到:
该逻辑在前面讲JDK1.6对synchronized关键字的优化《【并发编程】 — synchronized锁的升级过程 + JDK1.6对synchronized关键字的其他优化简介 》时也讲到过 — 但由于没法调试,我们其实很难真正去了解它到底是怎么实现的。
接下来我们从源码角度看看Doug Lea大神的具体实现方式。
在介绍Doug Lea大神的具体实现方式之前先来介绍一下Node的具体数据结构,因为只有知道了里面的一个变量waitStatus,才能更好的明白其实现原理。
Node的结构如下:
概括起来可以分为三个部分
(1)线程的2种等待模式:
(2)线程在队列中的状态枚举:
(3)成员变量:
注意:
只有未获得锁,或者说被阻塞的方法才会进入Node链表中 , 已经获得锁的线程肯定不会进入Node链表。
知道了Node的数据结构之后,还必须知道Reentrantlock的公平锁和和非公平锁都是独占锁 —》因此在不涉及到线程交互的情况下,公平锁的waitStatus只可能有三个值,即1 --- CANCELLED、-1 --- SIGNAL 和 0 ---初始化Node时的默认值。
—> 这一点务必要记住!
具体流程总结如下:
自认为Reentrantlock公平锁的加锁过程被我这么一总结,还挺好理解的(☆_☆) ,为了更深刻一些,这里再做一个总结:
如果让我再用几句更简单的话来总结的话,我会这样进行总结:
(1)排队时,队首肯定是一个Thread为null的Node,其他线程的Node每次入队时都排在队尾(并维护双向链表关系)
(2)自旋 + park的过程 —》 如上图,哈哈哈!!!
end!
【并发编程】 --- Reentrantlock源码解析2:公平锁加锁过程超详细解析
原文:https://www.cnblogs.com/augustuss/p/12693690.html