在java多線程中可以使用object.wait/notify來進行線程之間的通訊 wait會使當前線程進入等待狀態,notify會從等待線程中隨機解除其等待狀態
注意:
- 使用wait 必須進行try catch
- 記住調用wait或者notify方法必須采用當前鎖調用,即必須采用synchronized中的對象,wait的本意是暫時釋放掉對象鎖,讓別的需要此對象的代碼能夠有機會執行。
舉個例子
當線程B訪問某個共享資源時,想獲取資源的鎖對象,發現這個鎖已經被線程A拿到了,這個時候,線程B只能被掛起,等待線程A釋放鎖。
但是拿到鎖的線程A在執行的過程中,因為某些條件還不滿足,暫時不想繼續執行下去,想先等待一下(注意:是已經拿到鎖的線程A自己想主動等待的),希望等到某個條件滿足后,繼續執行任務。在同步代碼塊里,線程A必須先釋放鎖,線程B才有資格獲取鎖,進入同步代碼塊,執行代碼。等線程B執行完后,線程A需要的條件已經滿足,那么這個時候必須有一個通知機制,讓線程A從等待狀態變成執行狀態,繼續執行代碼。
所以線程之間要協調溝通,必須有一個等待機制和通知機制,在JAVA里面,對應的就是wait方法和notify方法。
Object的wait方法
synchronized (obj) {
while (condition does not ok){
obj.wait();
}
}
如果想讓線程A處于等待狀態,可以調用當前對象wait方法。wait方法一旦被調用,也就意味著:線程A已經獲得鎖了,而且能做的事情都已經做了,現在只能等待了,等待另外的同步操作執行某些代碼后,我才回來繼續干活。
注意:
永遠不要在循環之外調用wait方法
對于從wait中被notify的進程來說,它在被notify之后還需要重新檢查是否符合執行條件,如果不符合,就必須再次被wait,如果符合才能往下執行。所以:wait方法應該使用循環模式來調用。按照上面的生產者和消費者問題來說:錯誤情況一:如果有兩個生產者A和B,一個消費者C。當存儲空間滿了之后,生產者A和B都被wait,進入等待喚醒隊列。當消費者C取走了一個數據后,如果調用了notifyAll(),注意,此處是調用notifyAll(),則生產者線程A和B都將被喚醒,如果此時A和B中的wait不在while循環中而是在if中,則A和B就不會再次判斷是否符合執行條件,都將直接執行wait()之后的程序,那么如果A放入了一個數據至存儲空間,則此時存儲空間已經滿了;但是B還是會繼續往存儲空間里放數據,錯誤便產生了。錯誤情況二:如果有兩個生產者A和B,一個消費者C。當存儲空間滿了之后,生產者A和B都被wait,進入等待喚醒隊列。當消費者C取走了一個數據后,如果調用了notify(),則A和B中的一個將被喚醒,假設A被喚醒,則A向存儲空間放入了一個數據,至此空間就滿了。A執行了notify()之后,如果喚醒了B,那么B不會再次判斷是否符合執行條件,將直接執行wait()之后的程序,這樣就導致向已經滿了數據存儲區中再次放入數據。錯誤產生。