ReentrantLock

ReentrantLock内部主要有继承了AQS类的静态内部类Sync,还有继承此内部类的非公平内部类(NonfairSync)和公平内部类(FairSync),以及Sync类型的成员属性.该成员属性在构造方法执行时进行初始化赋值.无参构造为非公平,也可以通过fair的boolean类型参数进行构造初始化赋值.
ReentrantLock继承关系树为:
image.png

AQS

AQS类(AbstractQueuedSynchronizer)内部主要有静态内部Node类.
一些重要的成员属性:
head(指向Node队列头节点),tail(指向Node队列尾节点),state(0表示ReentrantLock对象是无锁状态,大于0表示对象已经被线程占有锁).
一些重要的成员方法:
enq(通过cas方式设置头节点或通过cas方式将包装当前线程的Node对象放入当前双向队列的尾部)
addWaiter(创建包装当前线程的Node对象,并通过cas方式将当前节点加入当前双向队列的尾部)
shouldParkAfterFailedAcquire(判断当前Node的前置Node的waitStatus的值,如果为-1则当前线程可以被park,如果为0则通过cas的方式将前置节点设置为-1,如果大于0,则需要找上一个Node的waitStatus不大于0的节点,将这些大于0的Node移出该双向队列)
parkAndCheckInterrupt(阻塞当前线程)
hasQueuedPredecessors(判断当前队列是否有元素,判断逻辑是头节点和尾节点不相等并且头节点的下一个节点为空或者下一个节点的线程不是当前线程对象时说明队列中有元素)
tryRelease(释放锁的方法,计算当前state的值减1后是否为0,如果为0就先将AQS的Thread类型的成员属性赋值为空,最后将state成员属性赋值为0(需要思考的是该方法内在state不为0前只可能是拥有锁的线程才能执行,所以不用去考虑多线程操作的问题);如果不为0说明该对象还没有全部解锁完成,所以不用去唤醒队列里面park的线程对象了)
unparkSuccessor(将头节点的ws值通过cas方式赋值为0,如果头节点的下一个节点不为空并且ws值不大于0,则unpark头节点的下一个节点里的线程对象;如果为空或者ws的值大于0,则直接从队列尾节点开始向前检查,检查到向前最靠近头节点的ws属性值不大于0的节点,然后唤醒该节点里的线程对象)

Node

Node类内部主要有数值类型的waitStatus(他的作用是标识下一个Node节点的是否被阻塞(-1),线程取消的状态(大于0),初始状态0),prev(Node),next(Node),thread(Thread)的成员属性.

NonfairSync和FairSync

非公平锁和公平锁,它们都是ReentrantLock类的静态内部类,父类都为ReentrantLock的另一个静态内部类Sync,Sync又继承AQS类.
非公平锁继承关系树:
image.png
公平锁继承关系树:
image.png
对比ReentrantLock的unLock方法,发现公平锁和非公平锁的释放锁的方法都是使用AQS的release方法:
如图:
image.png
该方法的主要是通过tryRelease方法去改变state的值,如果state的值减为0了,表示当前线程已经释放锁了,然后去判断当前head节点是否不为空并且头节点的ws状态值不等于0,就会去执行unparkSuccessor(主要是唤醒队列里面最靠近头节点并且ws值为有效状态节点的线程,具体方法描述见上面的说明).这个方法里面主要有两处没有理解的是如果头节点cas修改ws失败,那是否需要退出处理.还有ws一般是标注下一个节点的信号位,这边直接判断当前节点的信息位如果为取消,则需要从后向前找未被取消的节点不是很理解该操作.
该解锁方法和公平、非公平的acquireQueued方法形成对应关系
image.png
当执行shouldParkAfterFailedAcquire方法后,头节点的ws为-1,然后执行tryAcquire方法去获取锁,如果未获取说明state的值当前还是大于0(说明在其它线程未释放锁前和头节点的下一个节点park前,头节点的ws信号位已经为-1并且已经拥有了下一个节点了,这样就能保证释放锁的线程肯定可以执行unparkSuccessor方法去尝试唤醒队列中的线程),然后才会执行parkAndCheckInterrupt方法去阻塞当前线程.

公平与非公平的区别

因为释放锁流程是一致的,所以非公平锁和公平锁的区别主要在获取锁流程中,对比源码差异主要在lock和tryAcquire方法,如图:
image.png
对比lock方法,非公平锁是直接通过cas去尝试修改state的值来获取锁,失败调用AQS的acquire方法,该方法内部会调用tryAcquire方法.而公平锁是直接调用AQS的acquire方法.
对比tryAcquire方法,主要区别在无锁状态下,公平锁需要先去检查队列中是否有节点元素,如果没有才会再去cas尝试获取锁,而非公平锁直接cas去尝试获取锁.这也是公平锁和非公平锁两者关于公平性逻辑的具体表现.
image.png

总结:

关于非公平锁从代码实现原理来看,如果线程进入阻塞队列中,其唤醒还是会按照先入先出的顺序去唤醒,这里是可以体现公平性的.非公平锁的非公平性主要体现在新进入的线程和被释放锁线程唤醒的线程间的竞争关系.公平锁是只要阻塞队列中有元素,新进入的线程会直接去队列中排队而不会去竞争获取锁,体现了需要按时间进入的顺序去排队依次获取锁的公平性.

最后整理了ReentrantLock,Sync,NonfairSync,FairSync,AQS,Node的继承关系和一些重要成员属性和方法,如图:
image.png


有梦想的搬砖人
1 声望0 粉丝

系统设计 写代码开发 搬砖


« 上一篇
Lock介绍(二)