當Java類庫沒有提供適合的同步工具時,就需要構建自定義同步工具。
可阻塞狀態依賴操作的結構
有界緩存實現基類示例
if (++head == buf.length)
head = 0;
count--;
return v;
}
public final synchronized boolean isFull() {
return count == buf.length;
}
public final synchronized boolean isEmpty() {
return count == 0;
}
}
阻塞實現方式一:拋異常給調用者
分析:很難權衡休眠時間SLEEP_TIME設置。如果設置過小,CPU可能會輪詢多次,消耗CPU資源也越高;如果設置過大,響應性就越低。
阻塞實現方式三:條件隊列
條件隊列中的元素是一個個等待相關條件的線程。每個Java對象都可以作為一個鎖,每個對象同樣可以作為一個條件隊列,并且Object中的wait、notify、notifyAll方法就構成了內部條件隊列的API。Object.wait會自動釋放鎖,并請求操作系統掛起當前線程,從而使其它線程能獲得這個鎖并修改對象的狀態。Object.notify和Object.notifyAll能喚醒正在等待線程,從條件隊列中選取一個線程喚醒并嘗試重新獲取鎖。
使用條件隊列
1.條件謂詞
1).定義:條件謂詞是使某個操作成為狀態依賴操作的前提條件。條件謂詞是由類中各個狀態變量構成的表達式。例如,對于put方法的條件謂詞就是“緩存不為空”。
2).關系:在條件等待中存在一種重要的三元關系,包括加鎖、wait方法和一個條件謂詞。在條件謂詞中包含多個狀態變量,而每個狀態變量必須由一個鎖來保護,因此在測試條件謂詞之前必須先持有這個鎖。鎖對象和條件隊列對象(及調用wait和notify等方法所在的對象)必須是同一個對象。
3).約束:每次調用wait都會隱式地和特定的條件謂詞相關聯,當調用特定條件謂詞時,調用者必須已經持有與條件隊列相關的鎖,這個鎖必須還保護這組成條件謂詞的狀態變量
2.條件隊列使用規則
1).通常都有一個條件謂詞
2).永遠在調用wait之前測試條件謂詞,并且在wait中返回后再次測試;
3).永遠在循環中調用wait;
4).確保構成條件謂詞的狀態變量被鎖保護,而這個鎖必須與這個條件隊列相關聯;
5).當調用wait、notify和notifyAll時,要持有與條件隊列相關聯的鎖;
6).在檢查條件謂詞之后,開始執行被保護的邏輯之前,不要釋放鎖;
3.通知
盡量使用notifyAll,而不是nofify.因為nofify會隨機喚醒一個線程從休眠狀態變為Blocked狀態(Blocked狀態是種線程一直處于嘗試獲取鎖的狀態,即一旦發現鎖可用,馬上持有鎖),而notifyAll會喚醒條件隊列中所有的線程從休眠狀態變為Blocked狀態.考慮這么種情況,假如線程A因為條件謂詞Pa進入休眠狀態,線程B因為條件謂詞Pb進入休眠狀態.這時Pb為真,線程C執行單一的notify.如果JVM隨機選擇了線程A進行喚醒,那么線程A檢查條件謂詞Pa不為真后又進入了休眠狀態.從這以后再也沒有其它線程能被喚醒,程序會一直處于休眠狀態.如果使用notifyAll就不一樣了,JVM會喚醒條件隊列中所有等待線程從休眠狀態變為Blocked狀態,即使隨機選出一個線程一因為條件謂詞不為真進入休眠狀態,其它線程也會去競爭鎖從而繼續執行下去.
4.狀態依賴方法的標準形式
notifyAll();
}
顯示Condition對象
顯示的Condition對象是一種更靈活的選擇,提供了更豐富的功能:在每個鎖上可以存在多個等待,條件等待可以是中斷的獲不可中斷的,基于時限的等待,以及公平的或非公平的隊列操作。一個Condition可以和一個Lock關聯起來,就像一個條件隊列和一個內置鎖關聯起來一樣。要創建一個Condition,可以在相關聯的Lock上調用Lock.newCondition方法。以下用顯示條件變量重新實現有界緩存
public void doPut(V v) throws InterruptedException {
try {
lock.lock();
while (count == buf.length)
notFullCondition.await();
buf[tail] = v;
if (++tail == buf.length)
tail = 0;
count++;
notEmptyCondition.signal();
} finally {
lock.unlock();
}
}
public V doTake() throws InterruptedException {
try {
lock.lock();
while (count == 0)
notEmptyCondition.await();
V v = buf[head];
buf[head] = null;
if (++head == buf.length)
head = 0;
count--;
notFullCondition.signal();
return v;
} finally {
lock.unlock();
}
}
}
新聞熱點
疑難解答