線程的阻塞和喚醒在多線程並發過程中是一個關鍵點,當線程數量達到很大的數量級時,並發可能帶來很多隱蔽的問題。如何正確暫停一個線程,暫停後又如何在一個要求的時間點恢復,這些都需要仔細考慮的細節。Java為我們提供了多種API來對線程進行阻塞和喚醒操作,比如suspend與resume、sleep、wait與notify以及park與unpark等等。
01
睡眠
控制線程阻塞與喚醒的最簡單方式就是sleep了,Java通過sleep(n)方法能讓線程進入到阻塞等待狀態,直到休眠時間達到指定值後自動喚醒。該方法簡單也常用,但這種方式比較死板,需要我們預先確定線程進入阻塞的時間。而有些場景實際上我們根本沒辦法確定睡眠時間,這是sleep方式的最大劣勢。
sleep的使用很簡單,下面為一個例子。讓當前線程睡眠2000ms,最終輸出為"Sleep time in ms = 2000"。
02
掛起與恢復
在Java發展史上曾經使用suspend()、resume()方法對於線程進行阻塞喚醒,它能夠在代碼中控制阻塞和喚醒的時間節點,比起sleep()方法更加靈活。比如線程啟動後在某個時間點需要讓它掛起,這可以使用suspend方法,而當要重新喚醒它時則使用resume方法。
下面代碼為例看suspend與resume組合的實現,Thread2啟動後輸出"Second thread is suspended itself",接著自己將自己掛起。在等待2000ms後由主線程resume恢復,然後Thread2繼續輸出"Second thread runs again"。
注意:suspend(),resume(),stop()這樣的方法都被標註為過期方法,因為其不會保證釋放資源,容易產生死鎖,所以不建議使用。
03
死鎖問題
suspend與resume的組合存在很多問題,比較典型的還是死鎖問題。如下代碼,主要的邏輯代碼是主線程啟動線程mt一段時間後嘗試使用suspend()讓線程掛起,最後通過resume()恢復線程。但現實並不如願,執行到suspend()時將一直卡住,你永遠等不來「can you get here?」的輸出。
為什麼會產生上面的現象呢?其實是由死鎖導致。乍一看感覺一點問題都沒有,線程的任務僅僅只是簡單地列印字符串。其實問題的根源隱藏得較深,主線程啟動了線程mt後,線程mt開始執行execute()方法,不斷列印字符串。
- END -