上次分析了ReentrantLock#lock()與ReentrantLock#unlock()的實現原理,並初步討論了AQS的等待隊列模型,參考源碼|併發一枝花之ReentrantLock與AQS(1):lock、unlock。java
本文繼續分析ReentrantLock#lockInterruptibly(),內容簡單,能夠跳過。node
JDK版本:oracle java 1.8.0_102git
public interface Lock {
...
void lock();
void lockInterruptibly() throws InterruptedException;
void unlock();
...
}
複製代碼
Lock#lockInterruptibly()是Lock#lock()的一個衍生品,解鎖方法也爲Lock#unlock()。所以,咱們只須要分析Lock#lockInterruptibly()。github
Lock#lockInterruptibly()方法比較特殊,當經過這個方法去獲取鎖時,若是線程正在等待獲取鎖,則這個線程可以響應中斷,即中斷線程的等待狀態。c#
一個典型用法以下:併發
當兩個線程同時經過Lock#lockInterruptibly()阻塞的獲取某個鎖時,假如此時線程A獲取到了鎖,則線程B只有繼續等待;此時,讓線程A對線程B調用threadB.interrupt()方法,可以中斷線程B的等待,讓線程B能夠先作其餘事。oracle
想對的,若是使用內置鎖synchronized,當一個線程處於等待某個鎖的狀態時,是沒法被中斷的,只有一直等待下去。這是可中斷鎖
最大的優點。ui
注意,當一個線程獲取了鎖以後,是不會被Thread#interrupt()方法中斷的。spa
基本的實現原理與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()與之類似,再也不分析。
本文連接:源碼|併發一枝花之ReentrantLock與AQS(2):lockInterruptibly
做者:猴子007
出處:monkeysayhi.github.io
本文基於 知識共享署名-相同方式共享 4.0 國際許可協議發佈,歡迎轉載,演繹或用於商業目的,可是必須保留本文的署名及連接。