一、典型例子:生產(chǎn)者消費(fèi)者
自行百度,不作贅述
二、業(yè)務(wù)場景
當(dāng)A線程對一個變量進(jìn)行修改,然后通知其他線程之后,B線程根據(jù)這個通知進(jìn)行觸發(fā)相應(yīng)的業(yè)務(wù)邏輯。
最笨的方法:我們首先會在B線程中:
while(條件不滿足){
check 條件不滿足
sleep(50);
}
但是這種做法有個問題,不能保證業(yè)務(wù)變化的及時性,即可能我剛開始休眠這個條件就已經(jīng)滿足了還需要等待固定的時間,有人就說:那我可以把休眠時間調(diào)整的盡可能小,那么對于我們的CPU的負(fù)載壓力就很大了,因此這種方式是有弊端的。
wait()
當(dāng)A線程的O對象調(diào)用wait()方法之后,進(jìn)入等待狀態(tài),只有當(dāng)B線程調(diào)用O對象的notify()或notifyAll()方法之后才會喚醒A線程。A喚醒之后做自己的相關(guān)業(yè)務(wù)工作。
notify()和notifyAll()
如果某些線程在等待某些條件觸發(fā),那當(dāng)那些條件為真時,你可以用 notify 和 notifyAll 來通知那些等待中的線程重新開始運(yùn)行。不同之處在于,notify 僅僅通知一個線程,并且我們不知道哪個線程會收到通知,然而 notifyAll 會通知所有等待中的線程。換言之,如果只有一個線程在等待一個信號燈,notify和notifyAll都會通知到這個線程。但如果多個線程在等待這個信號燈,那么notify只會通知到其中一個,而其它線程并不會收到任何通知,而notifyAll會喚醒所有等待中的線程。
三、等待和通知的標(biāo)準(zhǔn)范式
等待方要進(jìn)行等待時:
syn(對象){
while(條件不滿足){
對象.wait()
}
條件滿足了,做業(yè)務(wù)邏輯
}
通知方:
syn(對象){
改變條件
對象.notify()/notifyAll()
}
四、notify()和notifyAll()應(yīng)該用誰
notify()具有隨機(jī)性,因?yàn)椴恢酪獑拘涯囊粋€,所以在不確定要喚醒具體哪個線程的時候,最好是用notifyAll(),謹(jǐn)慎使用notify()。
五、市面上所有的連接池都是通過等待超時模式實(shí)現(xiàn)的
一個連接池中有固定的(比如10個)連接數(shù),如果在某一時刻并發(fā)量上來了,有50個請求需要訪問數(shù)據(jù)庫,那么只會有十個請求去成功地訪問到數(shù)據(jù)庫中,剩下的40個線程會進(jìn)入到一個等待狀態(tài),只有當(dāng)十個已經(jīng)連接數(shù)據(jù)庫的線程的業(yè)務(wù)處理完畢,釋放掉數(shù)據(jù)庫的連接,才會去通知剩余的40個線程,然后從剩余的40個連接中挑一個線程去進(jìn)行數(shù)據(jù)庫的連接。
六、調(diào)用yield()、sleep()、wait()、notify()等方法對鎖有何影響?
調(diào)用yield()、sleep()之后,當(dāng)前線程所持有的鎖是不會被釋放的
調(diào)用wait()方法之后會釋放當(dāng)前線程鎖持有的鎖,被喚醒之后會重新競爭鎖。
調(diào)用notify()后對鎖沒有任何影響,但是在調(diào)用notify()時,一定要放在一個同步塊里面,線程只有在執(zhí)行完整個同步塊后,才會自然而然釋放鎖,因此notify()一般放在代碼同步塊的最后一行。