park/unpark和wait/notify都是Java提供給我們的機制,讓我們能夠?qū)Ξ?dāng)前線程是否陷入阻塞進行控制。
park/unpark是LockSupport包中的功能,wait/notify是Java的Object自帶的api。
二者都提供了帶時間和不帶時間版本的api來阻塞/喚醒當(dāng)前線程。
區(qū)別
1. 是否需要持有鎖
wait/notify必須在synchronized代碼塊(持有當(dāng)前對象鎖)中調(diào)用,調(diào)用wait會釋放當(dāng)前對象的對象鎖,直到被其他線程通過notify/notifyAll喚醒.
park/unpark不需要在同步塊中調(diào)用,因此不需要持有鎖。并且park/unpark實現(xiàn)方式是最多數(shù)量為1的信號量機制,通過unpark會使得信號量恢復(fù)為1,調(diào)用park時如果當(dāng)前信號量數(shù)量大于0,就直接放行(信號量數(shù)量-1),否則才阻塞當(dāng)前線程。因為park/unpark并沒有持有鎖,因此當(dāng)線程被阻塞時也就不涉及到對象鎖釋不釋放的問題了。
不需要在同步塊中調(diào)用使得park/unpark機制對比wait/notify來說,是更靈活的一種機制。
2. 對中斷的響應(yīng)處理
當(dāng)前線程因為調(diào)用wait陷入阻塞時,如果當(dāng)前線程被中斷,此時wait阻塞會被喚醒,并拋出InterruptedException.
當(dāng)線程因為park調(diào)用陷入阻塞時,如果當(dāng)前線程被中斷,此時被park阻塞的線程會被喚醒,但不會拋出InterruptedException,調(diào)用者需要通過 Thread.isInterrupted來檢查當(dāng)前線程是否被中斷了來響應(yīng)中斷。
3. 喚醒信號丟失問題
對于wait/notify來說,如果notify發(fā)生在wait之前,這個時候就可能發(fā)生喚醒信號丟失,也就是說線程會在wait處陷入阻塞。因此wait/notify需要搭配外部狀態(tài)變量來使用,避免喚醒信號丟失問題。
對于park/unpark來說,由于實現(xiàn)方式是通過信號量機制來實現(xiàn)的。一旦通過unpark將信號量恢復(fù)了,使用park時就不會阻塞,因此喚醒信號不會丟失。
如何選擇?
對于簡單的生產(chǎn)者-消費者模型,可以直接使用wait/notify,對于比較復(fù)雜的同步模型,我們通??梢允褂脳l件變量和其他并發(fā)包下的數(shù)據(jù)結(jié)構(gòu),直接使用LockSupport的機會一般來說比較少,除非現(xiàn)有同步原語不滿足需求,我們需要更精細的控制,這個時候可能會去使用LockSupport。