上次分析了 ReentrantLock#lock() 与 ReentrantLock#unlock() 的实现原理,并初步讨论了 AQS 的等待队列模型,参考 源码 | 并发一枝花之 ReentrantLock 与 AQS(1):lock、unlock 。
本文继续分析 ReentrantLock#lockInterruptibly(),内容简单,可以跳过。
JDK 版本:oracle java 1.8.0_102
- public interface Lock {
- ...
- void lock();
- void
- lockInterruptibly
- ()
- throws
- InterruptedException
- ;
- void unlock();
- ...
- }
Lock#lockInterruptibly() 是 Lock#lock() 的一个衍生品,解锁方法也为 Lock#unlock()。因此,我们只需要分析 Lock#lockInterruptibly()。
Lock#lockInterruptibly() 方法比较特殊,当通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。
一个典型用法如下:
当两个线程同时通过 Lock#lockInterruptibly() 阻塞的获取某个锁时,假如此时线程 A 获取到了锁,则线程 B 只有继续等待;此时,让线程 A 对线程 B 调用 threadB.interrupt() 方法,能够中断线程 B 的等待,让线程 B 可以先做其他事。
想对的,如果使用内置锁 synchronized,当一个线程处于等待某个锁的状态时,是无法被中断的,只有一直等待下去。这是可中断锁最大的优势。
注意,当一个线程获取了锁之后,是不会被 Thread#interrupt() 方法中断的。
基本的实现原理与 ReentrantLock#lock() 相同。
仍以默认的非公平策略为例进行分析。
- public
- void
- lockInterruptibly
- ()
- throws
- InterruptedException
- {
- sync.acquireInterruptibly(1);
- }
回忆 ReentrantLock 的核心思想:用 state 表示 "所有者线程已经重复获取该锁的次数"。
- public abstract class AbstractQueuedSynchronizer
- extends AbstractOwnableSynchronizer
- implements java.io.Serializable {
- ...
- public final void acquireInterruptibly(int arg)
- throws InterruptedException {
- if (Thread.interrupted())
- throw new InterruptedException();
- if (!tryAcquire(arg))
- doAcquireInterruptibly(arg);
- }
- ...
- }
改写:
- public abstract class AbstractQueuedSynchronizer
- extends AbstractOwnableSynchronizer
- implements java.io.Serializable {
- ...
- public final void acquireInterruptibly(int arg)
- if (Thread.interrupted())
- throw new InterruptedException();
- if (tryAcquire(arg)) {
- return;
- }
- doAcquireInterruptibly(arg);
- }
- ...
- }
首先,通过 tryAcquire() 尝试获取锁。如果获取成功,则直接返回。如果获取失败,则借助 doAcquireInterruptibly 实现可中断获取。
NonfairSync#tryAcquire() 在 ReentrantLock#lock() 中分析过了,直接看 AQS#doAcquireInterruptibly()。
- public abstract class AbstractQueuedSynchronizer
- extends AbstractOwnableSynchronizer
- implements java.io.Serializable {
- ...
- private void doAcquireInterruptibly(int arg)
- throws InterruptedException {
- final Node node = addWaiter(Node.EXCLUSIVE);
- boolean failed = true;
- try {
- for (;;) {
- final Node p = node.predecessor();
- if (p == head && tryAcquire(arg)) {
- setHead(node);
- p.next = null; // help GC
- failed = false;
- return;
- }
- if (shouldParkAfterFailedAcquire(p, node) &&
- parkAndCheckInterrupt())
- throw new InterruptedException();
- }
- } finally {
- if (failed)
- cancelAcquire(node);
- }
- }
- ...
- }
同 AQS#acquireQueued() 基本相同,唯一的区别是对中断信号的处理。
AQS#acquireQueued() 被中断后,将中断标志传给外界,外界再调用 Thread#interrupt() 复现中断;而 AQS#doAcquireInterruptibly() 则直接抛出 InterruptedException。
二者本质上没什么不同。但 AQS#doAcquireInterruptibly() 显示抛出了 InterruptedException,调用者必须处理或继续上抛该异常。
可以看到,分析完 ReentrantLock#lock() 后,其衍生品 ReentrantLock#lockInterruptibly() 的分析也变得极其简单。ReentrantLock#tryLock() 与之相似,不再分析。
来源: https://juejin.im/post/5a406f046fb9a045076fe544