一、ReentrantLock 中断原理
1. ReentrantLock 不可打断模式
- lock()方法是不可打断的,即使它被打断(只会有一个打断标记),仍会驻留在 AQS 队列中,一直要等到获得锁后才能得知自己被打断了
执行流程:
- 执行lock();
- CAS失败后,执行acquire(1);
- tryAcquire(1)再次失败,执行acquireQueued();
- tryAcquire(1)又一次失败,并且满足线程挂起条件,执行parkAndCheckInterrupt();
4.1 LockSupport.park(this); park后会在此处阻塞;
4.2 park时,如果被打断,则返回true并清除打断标记(Thread.interrupted();返回true,并将打断标记置为false); - 如果是因为被打断而唤醒, 记录打断状态为true(interrupted = true),再次进入循环进行tryAcquire获得锁后, 才能返回打断状态;
- 再次进入循环获得锁后,将第5步的打断状态true返给第7步;
- 第6步返回true说明:经历过中断并获取到锁,执行selfInterrupt()方法再执行一次中断;
7.1 重新产生一次中断,将打断标记置为true,后面再执行LockSupport.park(this);就不会阻塞了;
// 这是ReentrantLock中的lock()方法
public void lock() {
// 调用非公平锁同步器中的lock()方法
sync.lock();
}
// 这是ReentrantLock中非公平锁的同步器
static final class NonfairSync extends Sync {
// 1.执行lock()
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
// 2.CAS失败后,执行acquire(1)
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
// ====================以下都是AQS中的方法=======================
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
// 3.tryAcquire(1)再次失败,执行acquireQueued()
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
// 7.执行到这里说明:经历过中断并获取到锁,进入selfInterrupt方法再执行一次中断
selfInterrupt();
}
static void selfInterrupt() {
// 7.1 重新产生一次中断,将打断标记置为true,后面再执行LockSupport.park(this);就不会阻塞了
Thread.currentThread().interrupt();
}
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
// 6.再次进入循环获得锁后,返回打断状态true
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
// 4.tryAcquire(1)又一次失败,并且满足线程挂起条件,执行parkAndCheckInterrupt()
parkAndCheckInterrupt())
// 5.如果是因为被打断而唤醒, 记录打断状态为true,再次进入循环进行tryAcquire获得锁后, 才能返回打断状态
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private final boolean parkAndCheckInterrupt() {
// 4.1 park后会在此处阻塞
// 如果打断标记已经是 true, 则 park 会失效
LockSupport.park(this);
// 4.2 park时,如果被打断,则返回true并清除打断标记(将打断标记置为false)
return Thread.interrupted();
}
2. ReentrantLock 可打断模式
- lockInterruptibly()方法是可打断的,被打断后直接抛出中断异常
执行流程:
- 执行lockInterruptibly();
- 调用AQS中的acquireInterruptibly(1);
- tryAcquire(1)获取锁失败,执行doAcquireInterruptibly(1);
- 执行parkAndCheckInterrupt(),LockSupport.park(this);如果被 interrupt中断,返回true执行第5步
- throw new InterruptedException();被打断后会直接抛出中断异常(此时就是与不可打断模式的区别)
// 这是ReentrantLock中的lockInterruptibly()方法
// 1.执行lockInterruptibly();
public void lockInterruptibly() throws InterruptedException {
// 2.调用AQS中的acquireInterruptibly(1);
sync.acquireInterruptibly(1);
}
// AQS中的acquireInterruptibly()、doAcquireInterruptibly()方法
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer {
public final void acquireInterruptibly(int arg) throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
// 3.tryAcquire(1)获取锁失败,执行doAcquireInterruptibly(1);
doAcquireInterruptibly(arg);
}
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) &&
// 4.执行parkAndCheckInterrupt(),LockSupport.park(this);如果被 interrupt中断,返回true
parkAndCheckInterrupt())
// 5.此时会直接抛出异常, 而不会再次进入for (;;)死循环
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
}
private final boolean parkAndCheckInterrupt() {
// park后会在此处阻塞
LockSupport.park(this);
// park时,如果被打断,则返回true并清除打断标记(将打断标记置为false)
return Thread.interrupted();
}