天天看点

32张图带你了解ReentrantLock&AQS源码

1.ReentrantLock加锁过程

reentrantLock的加锁方式是调用lock方法

而真正调用的是sync.lock()

32张图带你了解ReentrantLock&AQS源码
32张图带你了解ReentrantLock&AQS源码

sync默认是一个非公平锁NonfairSync (可以插队),与其说是非公平锁,其实是一个非公平队列(也就是AQS)

32张图带你了解ReentrantLock&AQS源码
32张图带你了解ReentrantLock&AQS源码

所以加锁的时候真正调用的是下图中的NonfairSync 的lock方法

32张图带你了解ReentrantLock&AQS源码

1.1加锁具体过程

在下图中加锁的时候会先检查锁的状态(state),这里state是一个很重要的概念,state代表这里的锁是否已经被别人获取了,0代表没有,>1则代表已经加锁,这里>1是因为一个线程可以获取方法中的锁多次(例如迭代的情况),state是几就是几次,这就是可重入锁的由来。

这里是一个cas操作。

32张图带你了解ReentrantLock&AQS源码

具体实现如下图,调用的是native方法,本质是C++写的方法

32张图带你了解ReentrantLock&AQS源码
32张图带你了解ReentrantLock&AQS源码

上图中的参数中stateOffset是指state属性在内存中的位置,具体如下图所示

32张图带你了解ReentrantLock&AQS源码

如果锁还没有被获取那么线程获取锁(独占锁),如果锁已经被获取了,那么就去排队(或插队),如下图所示

32张图带你了解ReentrantLock&AQS源码

acquire方法如下,在去AQS排队之前,还是调用TryAcquire方法尝试去获取锁,这就是插队

32张图带你了解ReentrantLock&AQS源码

TryAcquire方法在NonfairSync中被重写

32张图带你了解ReentrantLock&AQS源码

实际上是调用

32张图带你了解ReentrantLock&AQS源码

尝试获取锁的过程如下,如果这时候成功获取锁,那么就不需要排队了

32张图带你了解ReentrantLock&AQS源码

当获取锁失败的时候执行acquireQueued方法,也就是排队获取锁

32张图带你了解ReentrantLock&AQS源码

在执行acquireQueued方法之前会调用addWaiter方法把当前线程封装成Node类,Node类结构如下,其中waitStatus标明了Node的模式(独占或者共享)

32张图带你了解ReentrantLock&AQS源码

AddWaiter方法如下

32张图带你了解ReentrantLock&AQS源码

Enq方法如下,节点的链表结构完成之后,节点就已经排好队了

32张图带你了解ReentrantLock&AQS源码

接下来就是用新建的Node去获取锁了,调用acquireQueued方法,具体过程如下

32张图带你了解ReentrantLock&AQS源码

尝试去获取锁在前面已经介绍了,获取失败后会去调用shouldParkAfterFailedAcquire方法如下图

32张图带你了解ReentrantLock&AQS源码

设置设置好节点之后调用parkAndCheckInterrupt方法挂起该节点,如下图

32张图带你了解ReentrantLock&AQS源码

至于为什么要返回当前线程是否被中断,因为线程挂起的时候是无法响应中断的,必须等线程重新启用后才能处理中断,下图是处理中断的地方,在完成锁的获取之后根据中断状态决定当前线程是否继续执行下去。

32张图带你了解ReentrantLock&AQS源码

以上是Node等待获取锁的过程,当Node没有获取到锁,并且又退出了自旋,那么会调用

cancelAcquire方法

32张图带你了解ReentrantLock&AQS源码

其方法如下

32张图带你了解ReentrantLock&AQS源码
32张图带你了解ReentrantLock&AQS源码

节点唤醒方式如下

32张图带你了解ReentrantLock&AQS源码
32张图带你了解ReentrantLock&AQS源码

至于为什么从尾部节点开始遍历,原因如下

32张图带你了解ReentrantLock&AQS源码

以上所有的步骤是获取锁,获取不到就被挂起,等待唤醒。当持有锁的线程unLock的时候,排队中的线程就会被唤醒,下面来看一下unLock方法

32张图带你了解ReentrantLock&AQS源码

锁调用了release方法,如下

32张图带你了解ReentrantLock&AQS源码

首先会tryRelease,这个方法在ReentrantLock的内部类sync中被重写,如下

32张图带你了解ReentrantLock&AQS源码

非重入锁直接解锁,使用unparkSuccessor方法唤醒下一个节点,该方法上面已经解释过了

32张图带你了解ReentrantLock&AQS源码

到此,加锁,排队,解锁的过程都已经结束。