ReentrantLock源码之解锁
解锁过程调用ReentrantLock.unlock()方法
如果有三个线程t1、t2、t3。t1线程持有锁之后,t2、t3线程入队。加锁之后的AQS状态图如下:
image.png
t1线程调用ReentrantLock.unlock()方法,会调用下面过程。
unlock
ReentrantLock#unlock
image.png
release
AbstractQueuedSynchronizer#release
image.png
tryRelease
调用到AbstractQueuedSynchronizer的tryRelease()方法,具体实现ReentrantLock.Sync#tryRelease
image.png
exclusiveOwnerThread表示独占模式同步的当前拥有者。
这个方法,首先获取state减去releases的值1,并判断c是否等于0,如果不等于0则表示是重入锁需要多次释放。释放一次,通过setState(c)设置一次state的值。最终释放当前线程,state值是0并返回true
release
image.pngt1线程释放锁state改为0。if条件成立。把head节点赋值给h,并判断是否需要unpark唤醒线程。
首先需要了解当前队列的waitStatus值。根据t1、t2、t3线程当前的ASQ队列图显示。当前一直是t1线程持有,调用解锁方法之前。t2线程park之前,把head节点waitStatus设置为-1。t3线程park前,把t2线程waitStatus设置为-1。t3线程的waitStatus没有后续线程进入,是tail节点waitStatus的值是初始状态0。
解析判断条件h != null && h.waitStatus != 0
如果h = null则头结点为空说明AQS队列为空,没有需要唤醒的线程。h != null&&h.waitStatus != 0说明后续有在park的线程,所以需要唤醒。调用unparkSuccessor()方法
unparkSuccessor
image.png当前头结点的waitStatus值是-1,首先通过compareAndSetWaitStatus方法把当前节点waitStatus设置为0。然后获取头结点的(next)下一个节点,调用 LockSupport.unpark(s.thread);方法唤醒这个线程所属的节点。即t2线程
image.png
unpark
LockSupport#unpark 调用UNSAFE的native方法
image.png
parkAndCheckInterrupt
AbstractQueuedSynchronizer#parkAndCheckInterrupt
唤醒t2线程,将会从调用park的地方继续执行(在加锁过程,加锁失败入队后)
image.png
检查isInterrupted标志位是否被改变。
acquireQueued
AbstractQueuedSynchronizer#acquireQueued 继续执行唤醒后的线程
image.png
释放t2线程的node节点
image.png
然后唤醒t3线程经历同样的过程。在释放t3线程的Node节点后由于t3的Node的waitStatus=0,不需要唤醒其他线程。
interrupt()方法
线程线程的Thread.interrupt()方法是中断线程。中断是在线程完成它的任务之前,停止它当前正在执行的操作。分以下几种情况:
1、线程阻塞在调用wait()、join()、sleep(long)、park()方法时,将会清除线程的中断状态并抛出InterruptedException异常
2、线程阻塞在InterruptibleChannel的IO上,Channel将会被关闭,线程被置为中断状态并抛出ClosedByInterruptException
3、线程阻塞在Selector,线程被置为中断状态,select方法会马上返回,类似Selector调用wakeup
4、如果不是以上三种情况,thread.interrupt()方法是设置线程的中断状态值。
/**
* Interrupts this thread.
*
* <p> Unless the current thread is interrupting itself, which is
* always permitted, the {@link #checkAccess() checkAccess} method
* of this thread is invoked, which may cause a {@link
* SecurityException} to be thrown.
*
* <p> If this thread is blocked in an invocation of the {@link
* Object#wait() wait()}, {@link Object#wait(long) wait(long)}, or {@link
* Object#wait(long, int) wait(long, int)} methods of the {@link Object}
* class, or of the {@link #join()}, {@link #join(long)}, {@link
* #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)},
* methods of this class, then its interrupt status will be cleared and it
* will receive an {@link InterruptedException}.
*
* <p> If this thread is blocked in an I/O operation upon an {@link
* java.nio.channels.InterruptibleChannel InterruptibleChannel}
* then the channel will be closed, the thread's interrupt
* status will be set, and the thread will receive a {@link
* java.nio.channels.ClosedByInterruptException}.
*
* <p> If this thread is blocked in a {@link java.nio.channels.Selector}
* then the thread's interrupt status will be set and it will return
* immediately from the selection operation, possibly with a non-zero
* value, just as if the selector's {@link
* java.nio.channels.Selector#wakeup wakeup} method were invoked.
*
* <p> If none of the previous conditions hold then this thread's interrupt
* status will be set. </p>
*
* <p> Interrupting a thread that is not alive need not have any effect.
*
* @throws SecurityException
* if the current thread cannot modify this thread
*
* @revised 6.0
* @spec JSR-51
*/
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
}
加锁方法lock
当调用了interrupt()方法
AbstractQueuedSynchronizer#acquireQueued方法返回interrupted=true
image.png
AbstractQueuedSynchronizer#acquire 调用selfInterrupt()方法
image.png
AbstractQueuedSynchronizer#selfInterrupt 再次调用interrupt()方法设置线程的中断状态值
image.png
加锁方法lockInterruptibly
当调用了interrupt()方法
ReentrantLock#lockInterruptibly
image.png
image.png
直接抛出InterruptedException中断异常
image.png
总结:
ReentrantLock重入锁的解锁过程。调用unlock解锁方法。把state值释放一次重新设置一次减1后的值。线程解锁完(state=0),根据AQS队列来判断是否需要释放下一个线程的Node节点,有下一个则调用unpark方法释放。线程继续从park的地方继续调用循环并重置head节点。
调用interrupt()方法,加锁方法lock和lockInterruptibly的区别