不可打斷模式
平時加鎖使用的
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);
}
}
總結
-
加鎖,線程是不可被打斷的,因為即便 T1被打斷了,它拿不到鎖,從循環裡出不來,依舊又會 park。reentrantLock.lock()
-
加鎖,線程是能可被打斷的,park 的線程一旦被打斷,直接抛異常。reentrantLock.lockInterruptibly()