CountDownLatch介紹3-之Condition

Condition只是一個接口,怎麼可以直接調用它的方法呢?於是查源碼(發現這個東西纔是最徹底的),從ReentrantLock開始,
ReentrantLock.newCondition()
->Sync.newCondition() Sync是一個靜態抽象的內部類
->ConditionObject 看來這個類最終實現了Condition接口
->AbstractQueuedSynchronizer.ConditionObject 抓到了
看到了詳細的方法實現 ^_^

1.兩三個線程同時修改某個對象,如果僅由訪問先後來決定結果的話,會出現各種結果。這種情況被稱爲racecondition。

2. 防止這種情況的發生,必須知道如何同步訪問(synchronize theaccess)。

3. javap -c -v ClassName可以反編譯一個.class文件。

4. 鎖住對象
早期版本的Java使用synchronized關鍵詞,JDK5以後引入了ReentrantLock類。
使用ReentrantLock類保護代碼塊的基本輪廓:
myLock.lock(); // a ReentrantLock object
try
{
critical section
}
finally
{
myLock.unlock(); // make surethe lock is unlocked even if an exception is thrown
}
這樣第一個線程調用lock方法鎖住myLock後,第二個線程調用lock方法就會被block,並且得等到第一個線程調用myLock.unlock()後才能繼續。
ReentrantLock類允許被多次鎖定,它記錄了呼叫的嵌套形式。大致是這個意思,原文是
The lock is called reentrant because a thread can repeatedlyacquire a lock that it already owns. The lock keeps a hold count that keeps track of the nestedcalls to the lockunlock forevery call to lock in order torelinquish the lock. Because of this feature, code that isprotected by a lock can call another method that uses the samelocks. method. Thethread has to call
For example, the TRansfer method calls the getTotalBalance method, which also locks thebankLock object, which now hasa hold count of 2. When the getTotalBalancetransfer method exits, the hold count is 0,and the thread relinquishes the lock. method exits, the hold countis back to 1. When the
In general, you will want to protect blocks ofcode that require multiple operations to update or inspect a datastructure. You are then assured that these operations run tocompletion before another thread can use the same object.
5. Condition Objects
ConditonObjects用來管理得了訪問權,卻實際並不能做有用功的線程。
以銀行帳戶轉帳爲例,轉帳時要確定轉出源的資金數不少於要轉出的金額。
首先不能這樣簡單的寫代碼:
if (bank.getBalance(from) >= amount)
bank.transfer(from, to,amount);
這種代碼完全有可能在if語句判斷完成後,transfer之前停滯,這樣在調用transfer的時候,可能帳戶當前的資金已經不是if語句判斷那個時候的數目了。
也就是說,測試可行性和實際操作必須一起進行,之間不能有中斷。
可以用一個lock把測試和操作綁定起來:
public void transfer(int from, int to, int amount)
{
bankLock.lock();
try
{
while (accounts[from] < amount)
{
// wait
. . .
}
// transfer funds
. . .
}
finally
{
bankLock.unlock();
}
}
這樣還是有問題,帳戶資金不夠移出時,會困在while塊中等待資金撥入這個帳戶,但由於bankLock已經被鎖定,所以其他線程不能進行撥入操作,進入了死循環。這就是conditionobject產生的原因。
一個lock對象可以有一個或多個相關的conditionobject,可以通過newCondition方法獲得一個條件對象,通常用實際條件命名每個條件對象,如
class Bank
{
public Bank()
{
. . .
sufficientFunds = bankLock.newCondition();
}
. . .
private ConditionsufficientFunds;
}
當transfer方法發現當前資金不夠時,調用
sufficientFunds.await();
這樣當前線程就會停滯,並解除lock。
await方法調用後,線程進入對應Condition的等待區,直到另一個線程調用同一Condition的signalAll方法纔會解除block狀態,並等待再次被線程管理器激活。
如果一個線程調用了condition.await方法,卻沒有其他線程調用condition.signalAll,這個線程就進入了deadlock情況。如果所有其他的線程都進入了等待區,而最後一個線程也調用了condition.await,那麼整個程序就掛起了。
因此最好的調用signalAll的時機是在每次對象的狀態被改變,而這個改變有可能使得等待區的線程有進展的時候。如Bank中每次成功轉帳之後。
另一個方法,signal,僅僅從等待區中隨機選擇一個進程並釋放。
注意:線程只能在獲得了lock權以後才能調用condition的await,signal, 和signalAll。
發佈了157 篇原創文章 · 獲贊 1 · 訪問量 7611
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章