天天看點

Guarded Suspension模式:等待喚醒機制的規範實作

Guarded Suspension 模式

項目組團建要外出聚餐,我們提前預訂了一個包廂,然後興沖沖地奔過去,到那兒後大堂經理看了一眼包廂,發現服務員正在收拾,就會告訴我們:“您預訂的包廂服務員正在收拾,請您稍等片刻。”過了一會,大堂經理發現包廂已經收拾完了,于是馬上帶我們去包廂就餐。

我們等待包廂收拾完的這個過程和小灰遇到的等待 MQ 傳回消息本質上是一樣的,都是等待一個條件滿足:就餐需要等待包廂收拾完,小灰的程式裡要等待 MQ 傳回消息。

那我們來看看現實世界裡是如何解決這類問題的呢?現實世界裡大堂經理這個角色很重要,我們是否等待,完全是由他來協調的。通過類比,相信你也一定有思路了:我們的程式裡,也需要這樣一個大堂經理。的确是這樣,那程式世界裡的大堂經理該如何設計呢?其實設計方案前人早就搞定了,而且還将其總結成了一個設計模式:Guarded Suspension。所謂 Guarded Suspension,直譯過來就是“保護性地暫停”。那下面我們就來看看,Guarded Suspension 模式是如何模拟大堂經理進行保護性地暫停的。

下圖就是 Guarded Suspension 模式的結構圖,非常簡單,一個對象 GuardedObject,内部有一個成員變量——受保護的對象,以及兩個成員方法——get(Predicate p)和onChanged(T obj)方法。其中,對象 GuardedObject 就是我們前面提到的大堂經理,受保護對象就是餐廳裡面的包廂;受保護對象的 get() 方法對應的是我們的就餐,就餐的前提條件是包廂已經收拾好了,參數 p 就是用來描述這個前提條件的;受保護對象的 onChanged() 方法對應的是服務員把包廂收拾好了,通過 onChanged() 方法可以 fire 一個事件,而這個事件往往能改變前提條件 p 的計算結果。下圖中,左側的綠色線程就是需要就餐的顧客,而右側的藍色線程就是收拾包廂的服務員。

Guarded Suspension 模式結構圖

GuardedObject 的内部實作非常簡單,是管程的一個經典用法,你可以參考下面的示例代碼,核心是:get() 方法通過條件變量的 await() 方法實作等待,onChanged() 方法通過條件變量的 signalAll() 方法實作喚醒功能。邏輯還是很簡單的,是以這裡就不再詳細介紹了。

Guarded Suspension模式:等待喚醒機制的規範實作

class GuardedObject{

//受保護的對象

T obj;

final Lock lock =

new ReentrantLock();

final Condition done =

lock.newCondition();

final int timeout=1;

//擷取受保護對象

T get(Predicate p) {

lock.lock();

try {

//MESA管程推薦寫法

while(!p.test(obj)){

done.await(timeout,

TimeUnit.SECONDS);

}

}catch(InterruptedException e){

throw new RuntimeException(e);

}finally{

lock.unlock();

}

//傳回非空的受保護對象

return obj;

}

//事件通知方法

void onChanged(T obj) {

lock.lock();

try {

this.obj = obj;

done.signalAll();

} finally {

lock.unlock();

}

}

}