1.ReentrantLock加锁过程
reentrantLock的加锁方式是调用lock方法
而真正调用的是sync.lock()
sync默认是一个非公平锁NonfairSync (可以插队),与其说是非公平锁,其实是一个非公平队列(也就是AQS)
所以加锁的时候真正调用的是下图中的NonfairSync 的lock方法
1.1加锁具体过程
在下图中加锁的时候会先检查锁的状态(state),这里state是一个很重要的概念,state代表这里的锁是否已经被别人获取了,0代表没有,>1则代表已经加锁,这里>1是因为一个线程可以获取方法中的锁多次(例如迭代的情况),state是几就是几次,这就是可重入锁的由来。
这里是一个cas操作。
具体实现如下图,调用的是native方法,本质是C++写的方法
上图中的参数中stateOffset是指state属性在内存中的位置,具体如下图所示
如果锁还没有被获取那么线程获取锁(独占锁),如果锁已经被获取了,那么就去排队(或插队),如下图所示
acquire方法如下,在去AQS排队之前,还是调用TryAcquire方法尝试去获取锁,这就是插队
TryAcquire方法在NonfairSync中被重写
实际上是调用
尝试获取锁的过程如下,如果这时候成功获取锁,那么就不需要排队了
当获取锁失败的时候执行acquireQueued方法,也就是排队获取锁
在执行acquireQueued方法之前会调用addWaiter方法把当前线程封装成Node类,Node类结构如下,其中waitStatus标明了Node的模式(独占或者共享)
AddWaiter方法如下
Enq方法如下,节点的链表结构完成之后,节点就已经排好队了
接下来就是用新建的Node去获取锁了,调用acquireQueued方法,具体过程如下
尝试去获取锁在前面已经介绍了,获取失败后会去调用shouldParkAfterFailedAcquire方法如下图
设置设置好节点之后调用parkAndCheckInterrupt方法挂起该节点,如下图
至于为什么要返回当前线程是否被中断,因为线程挂起的时候是无法响应中断的,必须等线程重新启用后才能处理中断,下图是处理中断的地方,在完成锁的获取之后根据中断状态决定当前线程是否继续执行下去。
以上是Node等待获取锁的过程,当Node没有获取到锁,并且又退出了自旋,那么会调用
cancelAcquire方法
其方法如下
节点唤醒方式如下
至于为什么从尾部节点开始遍历,原因如下
以上所有的步骤是获取锁,获取不到就被挂起,等待唤醒。当持有锁的线程unLock的时候,排队中的线程就会被唤醒,下面来看一下unLock方法
锁调用了release方法,如下
首先会tryRelease,这个方法在ReentrantLock的内部类sync中被重写,如下
非重入锁直接解锁,使用unparkSuccessor方法唤醒下一个节点,该方法上面已经解释过了
到此,加锁,排队,解锁的过程都已经结束。