if (timeSoFar >= msecTimeout)
throw new TimeoutException ();
else
waitTime = timeout - timeSoFar;
}
else
break;
}
}
}
public final void announce() {
object_.notifyAll ();
}
}
使用方法介紹
本小節(jié)我們將對上一節(jié)給出的抽象基類WaitWithTiming的使用方法進行詳細的介紹。我們當然可以直接使得ActiveQueue繼承自WaitWithTiming,并實現(xiàn)相應(yīng)的抽象hook方法condition,但是這樣做有一個弊端,就是對于ActiveQueue我們只能夠?qū)崿F(xiàn)僅僅一個condition,如果我們要添加針對dequeue時隊列為空的條件判斷邏輯就無能為力了,因為WaitWithWaiting僅僅只有一個condition方法(其實,即使有多個也沒有辦法做到通用,因為不能對具體的應(yīng)用的需求進行假設(shè))。
我們推薦的使用方法是,根據(jù)具體應(yīng)用的需求,整理出需要的判斷條件,創(chuàng)建相應(yīng)的類來表示這些判斷條件,使這些用來表示具體判斷條件的類繼承自WaitWithTiming,這些類中具體的條件判斷邏輯的實現(xiàn)可以使用相應(yīng)的具體的應(yīng)用實體。比如:對于本文開始所列舉的應(yīng)用,我們需要的判斷條件為隊列為滿,所以我們可以定義一個QueueFullCondition類繼承自WaitWithTiming,在QueueFullCondition中實現(xiàn)抽象的hook方法condition的邏輯,在該邏輯中在使用ActiveQueue的isFull方法。使用這種委托的方法,我們就可以比較有效的解決一個對象同時需要多個判斷條件的問題(不同的判斷條件只需定義不同的子類即可)。相應(yīng)的UML結(jié)構(gòu)圖和關(guān)鍵代碼實現(xiàn)如下:
關(guān)鍵代碼片斷:
class QueueFullCondition extends WaitWithTiming
{
public QueueFullCondition (ActiveQueue aq)
{ super (aq); } // 為WaitWithTiming中的object_賦值
public boolean condition () {
ActiveQueue aq = (ActiveQueue) object_; //使用ActiveQueue來實現(xiàn)具體的判斷邏輯
return aq.isFull ();
}
}
class ActiveQueue {
...
public synchronized void enqueue(ClientRequest cr, long timeout)
throws InterruptedException, TimeoutException
{
//具有時限控制的等待
queueFullCondition_.timedWait (timeout);
// 把用戶請求添加進隊列
//喚醒等待在ActiveQueue上的線程
queueFullCondition_.announce ();
}
...
private QueueFullCondition queueFullCondition_ = new QueueFullCondition (this);
}
要注意的問題
如果讀者朋友仔細觀察的話,就會覺察到在WaitWithTiming類中的timedWait方法的定義中沒有添加synchronized關(guān)鍵字,這一點是非常關(guān)鍵的,因為是為了避免在編寫并發(fā)的Java應(yīng)用時一個常見的死鎖問題:嵌套的monitor。下面對于這個問題進行簡單的介紹,關(guān)于這一問題更為詳細的論述請參見參考文獻〔1〕。
什么是嵌套的monitor問題呢?嵌套的monitor是指:當一個線程獲得了對象A的monitor鎖,接著又獲得了對象B的monitor鎖,在還沒有釋放對象B的monitor鎖時,調(diào)用了對象B的wait方法,此時,該線程釋放對象B的monitor鎖并等待在對象B的線程等待隊列上,但是此時該線程還擁有對象A的monitor鎖。如果該線程的喚起條件依賴于另一個線程首先要獲得對象A的monitor鎖的話,就會引起死鎖,因為此時別的線程無法獲得上述線程還沒有釋放的對象A的monitor鎖,結(jié)果就出現(xiàn)了死鎖情況。一般的解決方案是:在設(shè)計時線程不要獲取對象B的monitor鎖,而僅僅使用對象A的monitor鎖。
針對我們前面列舉的例子,ActiveQueu可以類比為對象A,queueFullContion_可以類比為對象B,如果我們在timedWait方法前面添加上synchronized關(guān)鍵字,就有可能會發(fā)生上述的死鎖情況,因為當我們在調(diào)用ActiveQueu的enqueue方法中調(diào)用了queueFullContion_的timedWait方法后,如果隊列為滿,雖然我們釋放了queueFullContion_的monitor鎖,但是我們還持有ActiveQueue的monitor鎖,并且我們的喚醒條件依賴于另外一個線程調(diào)用ActivcQueue的dequeue方法,但是因為此時我們沒有釋放ActiveQueue的monitor鎖,所以另外的線程就無法調(diào)用ActiveQueu的dequeue方法,那么結(jié)果就是這兩個線程就都只能夠等待。
總結(jié)
本文對于Java中wait方法超時語意的模糊性進行了分析,并給出了一個比較通用的解決方案,本解決方案對于需要精確的超時語意的應(yīng)用還是無法很好的適用的,因為方案中所給出的關(guān)于超時計算的算法是不精確的。還有一點就是有關(guān)嵌套monitor的問題,在編寫多線程的Java程序時一定要特別注意,否則非常容易引起死鎖。其實,本文所講述的所有問題的根源都是由于Java對于wait方法超時語意實現(xiàn)的模糊性造成的,如果在后續(xù)的Java版本中對此進行了修正,那么本文給出的解決方案就是多余的了
throw new TimeoutException ();
else
waitTime = timeout - timeSoFar;
}
else
break;
}
}
}
public final void announce() {
object_.notifyAll ();
}
}
使用方法介紹
本小節(jié)我們將對上一節(jié)給出的抽象基類WaitWithTiming的使用方法進行詳細的介紹。我們當然可以直接使得ActiveQueue繼承自WaitWithTiming,并實現(xiàn)相應(yīng)的抽象hook方法condition,但是這樣做有一個弊端,就是對于ActiveQueue我們只能夠?qū)崿F(xiàn)僅僅一個condition,如果我們要添加針對dequeue時隊列為空的條件判斷邏輯就無能為力了,因為WaitWithWaiting僅僅只有一個condition方法(其實,即使有多個也沒有辦法做到通用,因為不能對具體的應(yīng)用的需求進行假設(shè))。
我們推薦的使用方法是,根據(jù)具體應(yīng)用的需求,整理出需要的判斷條件,創(chuàng)建相應(yīng)的類來表示這些判斷條件,使這些用來表示具體判斷條件的類繼承自WaitWithTiming,這些類中具體的條件判斷邏輯的實現(xiàn)可以使用相應(yīng)的具體的應(yīng)用實體。比如:對于本文開始所列舉的應(yīng)用,我們需要的判斷條件為隊列為滿,所以我們可以定義一個QueueFullCondition類繼承自WaitWithTiming,在QueueFullCondition中實現(xiàn)抽象的hook方法condition的邏輯,在該邏輯中在使用ActiveQueue的isFull方法。使用這種委托的方法,我們就可以比較有效的解決一個對象同時需要多個判斷條件的問題(不同的判斷條件只需定義不同的子類即可)。相應(yīng)的UML結(jié)構(gòu)圖和關(guān)鍵代碼實現(xiàn)如下:
關(guān)鍵代碼片斷:
class QueueFullCondition extends WaitWithTiming
{
public QueueFullCondition (ActiveQueue aq)
{ super (aq); } // 為WaitWithTiming中的object_賦值
public boolean condition () {
ActiveQueue aq = (ActiveQueue) object_; //使用ActiveQueue來實現(xiàn)具體的判斷邏輯
return aq.isFull ();
}
}
class ActiveQueue {
...
public synchronized void enqueue(ClientRequest cr, long timeout)
throws InterruptedException, TimeoutException
{
//具有時限控制的等待
queueFullCondition_.timedWait (timeout);
// 把用戶請求添加進隊列
//喚醒等待在ActiveQueue上的線程
queueFullCondition_.announce ();
}
...
private QueueFullCondition queueFullCondition_ = new QueueFullCondition (this);
}
要注意的問題
如果讀者朋友仔細觀察的話,就會覺察到在WaitWithTiming類中的timedWait方法的定義中沒有添加synchronized關(guān)鍵字,這一點是非常關(guān)鍵的,因為是為了避免在編寫并發(fā)的Java應(yīng)用時一個常見的死鎖問題:嵌套的monitor。下面對于這個問題進行簡單的介紹,關(guān)于這一問題更為詳細的論述請參見參考文獻〔1〕。
什么是嵌套的monitor問題呢?嵌套的monitor是指:當一個線程獲得了對象A的monitor鎖,接著又獲得了對象B的monitor鎖,在還沒有釋放對象B的monitor鎖時,調(diào)用了對象B的wait方法,此時,該線程釋放對象B的monitor鎖并等待在對象B的線程等待隊列上,但是此時該線程還擁有對象A的monitor鎖。如果該線程的喚起條件依賴于另一個線程首先要獲得對象A的monitor鎖的話,就會引起死鎖,因為此時別的線程無法獲得上述線程還沒有釋放的對象A的monitor鎖,結(jié)果就出現(xiàn)了死鎖情況。一般的解決方案是:在設(shè)計時線程不要獲取對象B的monitor鎖,而僅僅使用對象A的monitor鎖。
針對我們前面列舉的例子,ActiveQueu可以類比為對象A,queueFullContion_可以類比為對象B,如果我們在timedWait方法前面添加上synchronized關(guān)鍵字,就有可能會發(fā)生上述的死鎖情況,因為當我們在調(diào)用ActiveQueu的enqueue方法中調(diào)用了queueFullContion_的timedWait方法后,如果隊列為滿,雖然我們釋放了queueFullContion_的monitor鎖,但是我們還持有ActiveQueue的monitor鎖,并且我們的喚醒條件依賴于另外一個線程調(diào)用ActivcQueue的dequeue方法,但是因為此時我們沒有釋放ActiveQueue的monitor鎖,所以另外的線程就無法調(diào)用ActiveQueu的dequeue方法,那么結(jié)果就是這兩個線程就都只能夠等待。
總結(jié)
本文對于Java中wait方法超時語意的模糊性進行了分析,并給出了一個比較通用的解決方案,本解決方案對于需要精確的超時語意的應(yīng)用還是無法很好的適用的,因為方案中所給出的關(guān)于超時計算的算法是不精確的。還有一點就是有關(guān)嵌套monitor的問題,在編寫多線程的Java程序時一定要特別注意,否則非常容易引起死鎖。其實,本文所講述的所有問題的根源都是由于Java對于wait方法超時語意實現(xiàn)的模糊性造成的,如果在后續(xù)的Java版本中對此進行了修正,那么本文給出的解決方案就是多余的了

