執行緒之間協作 等待與通知

2021-06-18 03:56:56 字數 2716 閱讀 4007

在理解了執行緒之間可能存在相互衝突,以及怎樣避免衝突之後,下一步就是學習怎樣使線

程之間相互協作。這種協作關鍵是通過執行緒之間的握手來進行的,這種握手可以通過

object的方法wait( )和notify()來安全的實現。

呼叫sleep()的時候鎖並沒有被釋放,理解這一點很重要。另一方面,wait( )方法的

確釋放了鎖,這就意味著在呼叫wait()期間,可以呼叫執行緒中物件的其他同步控制方法。

當乙個執行緒在方法裡遇到了對wait()的呼叫的時候,執行緒的執行被掛起,物件上的鎖被

釋放。有兩種形式的wait( )。第一種接受毫秒作為引數,意思與 sleep()方法裡引數的意思

相同,都是指「在此期間暫停」   。不同之處在於,對於 wait():

1.在 wait( )期間鎖是釋放的。

2.你可以通過notify( )、notifyall(),或者時間到期,從wait( )中恢

復執行。

第二種形式的wait( )不要引數;這種用法更常見。wait( )將無限等待直到執行緒接收到

notify( )或者notifyall( )訊息。

wait( ), notify( ),以及notifyall( )的乙個比較特殊的方面是這些方法是基類

object的一部分,而不是像sleep( )那樣屬於thread的一部分。儘管開始看起來有點

奇怪,僅僅針對執行緒的功能卻作為通用基類的一部分而實現,不過這是有道理的,因為這

些功能要用到的鎖也是所有物件的一部分。所以,你可以把wait( )放進任何同步控制方

法里,而不用考慮這個類是繼承自thread還是實現了runnable介面。實際上,你只能在

同步控制方法或同步控制塊裡呼叫wait(), notify( )和notifyall()(因為不用

操作鎖,所以sleep()可以在非同步控制方法裡呼叫)。如果你在非同步控制方法裡呼叫

這些方法,程式能通過編譯,但執行的時候,你將得到illegalmonitorstateexception

異常,伴隨著一些含糊的訊息,比如「當前執行緒不是擁有者」。訊息的意思是,呼叫 wait( ),

notify( )和notifyall( )的執行緒在呼叫這些方法前必須「擁有」(獲取)物件的鎖。

你能夠讓另乙個物件執行這種操作以維護其自己的鎖。要這麼做的話,你必須首先得到對

象的鎖。比如,如果你要在物件x上呼叫notify( ),那麼你就必須在能夠取得x的鎖的同

步控制塊中這麼做:

synchronized(x)

特別地,當你在等待某個條件,這個條件必須由當前方法以外的因素才能改變的時候(典

條件的時候空等;這也稱為「忙等」,它會極大占用cpu時間。所以wait( )允許你在等待

外部條件的時候,讓執行緒休眠,只有在收到notify( )或notifyall()的時候執行緒才喚

例如,考慮乙個餐館,有乙個廚師和乙個服務員。服務員必須等待廚師準備好食物。當廚

師準備好食物的時候,他通知服務員,後者將得到食物然後繼續等待。這是乙個執行緒協作

的極好的例子:廚師代表了生產者,服務員代表了消費者。

waitperson(服務員)必須知道自己所工作的restaurant(餐館),因為他們必須從餐館

的「訂單視窗」取出訂單restaurant.order。在run( )中,waitperson呼叫wait( )

進入等待模式,停止執行緒的執行直到被chef(廚師)的notify( )方法所喚醒。因為是很

簡單的程式,我們知道只有乙個執行緒在等待waitperson物件的鎖:即waitperson執行緒

自己。正因為這個原因,使得呼叫notify()是安全的。在更複雜的情況下,多個執行緒可

能在等待同乙個特定的鎖,所以你不知道哪個執行緒被喚醒。解決方法是呼叫notifyall( ),

它將喚醒所有等待這個鎖的執行緒。每個執行緒必須自己決定是否對這個通知作出反應。

注意對wait( )的呼叫被包裝在乙個while( )語句裡,它在測試的正是等待的條件。開

始看起來可能很奇怪--如果你在等乙個訂單,那麼一旦你被喚醒,訂單必須是可用的,

對不對?問題是在多執行緒程式裡,一些別的執行緒可能在waitperson甦醒的同時衝進來搶

走訂單。唯一安全的方法就是對於wait()總是使用如下方式:

while(conditionisnotmet)

wait( );

這可以保證在你跳出等待迴圈之前條件將被滿足,如果你被不相干的條件所通知(比如

notifyall( )),或者在你完全退出迴圈之前條件已經被改變,你被確保可以回來繼續

等待。乙個chef物件必須知道他/她工作的餐館(這樣可以通過restaurant.order下訂單)和

取走食物的waitperson,這樣才能在訂單準備好的時候通知waitperson。在這個簡化

過的例子中,由chef產生訂單物件,然後通知waitperson訂單已經準備好了。

請注意對notify()的呼叫必須首先獲取waitperson物件的鎖。waitperson.run( )

裡對wait( )的呼叫將自動釋放這個鎖,所以這是可能的。因為要呼叫notify( )必須獲

取鎖,這就能保證如果兩個執行緒試圖在同乙個物件上呼叫notify( )時不會互相衝突。

上面的例子中,乙個執行緒只有乙個單一的地點來儲存某個物件,這樣另乙個執行緒就可以在

以後使用這個物件。然而,在乙個典型的生產者-消費者實現中,你要使用先進先出的隊

列來存放被生產和消費的物件。

執行緒通知與等待

當乙個執行緒呼叫乙個共享變數的 wait 方法時,該呼叫執行緒會被阻塞掛起,直到發生下面幾件事情之一才返回。其他執行緒呼叫了該共享物件的 notify 或者 notify 方法 其他執行緒呼叫了該執行緒的 interrupt 方法,該執行緒丟擲 interruptedexception 異常返回。注...

執行緒通知與等待

乙個執行緒呼叫乙個共享變數的wait 方法時,該執行緒會被阻塞掛起。呼叫wait的前提是該執行緒有獲取共享變數的監視器鎖。獲取監視器鎖 1 synchronize 共享變數 2 在在共享變數的方法前加synchronize關鍵字,呼叫該方法。若執行緒沒有獲取監視器鎖而呼叫了wait 則會丟擲ille...

執行緒之間的通訊 等待喚醒機制

執行緒間通訊 其實就是多個執行緒在操作同乙個資源,但是操作的動作不同。class res class input implements runnable public void run catch exception e if x 0 else x x 1 2 r.flag true r.notif...