不可打断模式
平时加锁使用的 reentrantLock.lock()
方法, 默认是不可打断模式,即便 park 状态的线程被打断了,它也不会立即响应,它仍旧在 AQS 中运行着,直到拿到锁,再自己中断自己。
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this); // 线程停在了这里。
// 获取当前线程是否被打断了,并且清空打断标记。
return Thread.interrupted();
}
假设 T1 处于 park 状态,但被 T2 拿到了 T1的线程对象,调用 t1.interrupt()
方法,将 t1 从打断中给唤醒了。parkAndCheckInterrupt() 返回 true。
final boolean acquireQueued(final Node node, int arg) {
// 拿锁失败?默认是。
boolean failed = true;
try {
boolean interrupted = false;
for (;;)
// 如果获得不了锁,T1 依旧会进入 park。
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null;
failed = false;
// T1 只有拿到锁时,才能跳出这个死循环。
// 返回 打断标记,true。
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
// 被打断的 T1 执行到这里。
interrupted = true;
// 死循环,回到上面去重新执行。
}
} finally {
if (failed)
cancelAcquire(node);
}
}
如果 T1 被打断后,拿到了锁,它返回到哪里了呢
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // acquireQueued() 返回结果true。
// 执行这个方法。
selfInterrupt();
}
static void selfInterrupt() {
// T1自己重新产生一次中断。
Thread.currentThread().interrupt();
}
可打断模式
调用 reentrantLock.lockInterruptibly()
那就是可打断的。
public void lockInterruptibly() throws InterruptedException {
// 调用 AQS 的 acquireInterruptibly(1) 方法。
sync.acquireInterruptibly(1);
}
public final void acquireInterruptibly(int arg)
throws InterruptedException {
// 如果当前线程被打断了,直接抛异常。
if (Thread.interrupted())
throw new InterruptedException();
// 尝试拿锁。
if (!tryAcquire(arg))
// 那锁失败,执行该方法。
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) &&
parkAndCheckInterrupt())
// park 的线程被打断后,直接抛异常。
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
总结
reentrantLock.lock()
加锁,线程是不可被打断的,因为即便 T1被打断了,它拿不到锁,从循环里出不来,依旧又会 park。reentrantLock.lockInterruptibly()
加锁,线程是能可被打断的,park 的线程一旦被打断,直接抛异常。