java并发包系列---LockSupport
長久以來對線程阻塞與喚醒經常我們會使用object的wait和notify,除了這種方式,java并發包還提供了另外一種方式對線程進行掛起和恢復,它就是并發包子包locks提供的LockSupport。
LockSupport提供了park和unpark進行線程的掛起和恢復操作,來看一個簡單掛起和恢復的簡單例子:
由于編輯格式限制,直接貼代碼有人反映會顯得很雜亂,之后有關代碼將直接放圖片,上例子中描述了一個場景,周末了某男興奮的去打游戲了,于是被游戲阻塞了(park),其他的都不干了,這個時候女朋友打來電話,別打游戲了,陪她逛街,把男朋友從游戲中喚醒(unpark)。LockSupport使用方式和wait/notify很類似,LockSupport使用更加靈活,unpark可以先于park進行調用,因為這個特點,我們可以不用擔心掛起和恢復時序問題,就如流打開了必須關閉這中類似問題,給我們帶來很多編程麻煩。
LockSupport底層是有Unsafe提供的兩個基本同步語句,這個在關于Unsafe介紹中已經做了分析,LockSupport是對這兩個函數的進一步封裝,除了例子中方法,它還提供了其他幾個功能。
以上為LockSupport屬性,Unsafe這個很好理解,整個LockSupport的實現都是基于Unsafe的兩個方法,SEED、PROBE、SECONDARY都是Thread類中為了對象上圖中三個字段的相對地址偏移量,功能主要用于ThreadLocalRandom類進行隨機數生成,它比Random性能要高的多,可閱讀該篇文章了解詳細(https://my.oschina.net/adan1/blog/159371),雖然LockSupport定義這三個字段但是基本沒有使用,可能之后JDK會有所變化吧。這里parkBlockerOffset字段,解釋起來就是掛起線程對象的偏移地址,對應的是Thread類的parkBlocker。
這個字段可以理解為專門為LockSupport而設計的,它被用來記錄線程是被誰堵塞的,當程序出現問題時候,通過線程監控分析工具可以找出問題所在。注釋說該對象被LockSupport的getBlocker和setBlocker來獲取和設置,且都是通過地址偏移量方式獲取和修改的。由于這個變量LockSupport提供了park(Object parkblocker)方法。
代碼很好理解獲取當前線程,通過偏移量的方式設置parkBlocker的值,將調取unsafe.park把線程掛起,線程被恢復后,修改blocker為null。
那我們可以把文章開篇例子修改的更加應景一些,如下:
定義一個blocker者名字叫“游戲”,某男被游戲堵塞park(a),我們通過jstack pid獲取當前線程相關信息(jstack用于打印出給定的Java進程ID或core file或遠程調試服務的Java堆棧信息):
可以看到當前線程狀態是WAITING,確實通過unsafe.park掛起的,blocker為一個字符串類型的id為0x…的。通過mat工具可以知道這個id對應得對象即為變量名為game的string對象。若不設置blocker,則是空的,如下:
只是將線程進行了掛起,無blocker。
那么LockSupport的park/unpark與wait/notify有啥區別呢?首先wait/notify對線程所起的作用和park/unpark是一樣的,如下為使用wait阻塞線程的線程狀態:
也是waiting,只是方式是調取Object.wait。說明產生的效果是一樣,那來繼續分析一下兩者實現機制。
它倆面向操作的對象不同,通過上述分析,我們知道LockSupport阻塞和喚醒線程直接操作的就是線程,所以從語義上講,它更符合常理,或者叫更符合語義。而Object的wait/notify它并不是直接對線程操作,它是被動的方法,它需要一個object來進行線程的掛起或喚醒。
park/unpark使用起來會更加的靈活、方便。在調用對象的wait之前當前線程必須先獲得該對象的監視器(synchronized),被喚醒之后需要重新獲取到監視器才能繼續執行。而LockSupport則不會,如例子中所示,它可以隨意進行park或者unpark。
兩則雖然都能更改線程狀態為WAITING,但由于實現的機制不同,所以不會產生交集,就是park掛起線程,notify/notifyall是無法進行喚醒的。我們來看個例子。就好比你在打游戲,一個陌生大媽讓你逛街去,你應該不會去吧(特殊需求的除外)。如下例子(代碼這樣寫也是絕了,僅僅舉例子):
使用notify還有一個問題就是,有時候為了保險起見大多數都用notifyall,notify只能喚醒一個線程,假如有兩個被阻塞的話,另外一個就悲劇了。
除此之外,park也可以響應中斷異常,關于中斷異常詳講也需要一大篇文章,這里不做詳細分析,來看一下park響應中斷過程。
對開篇代碼改造一下,還是某男打游戲,且深深陷入其中(park),突然屎意甚濃,于是被其中斷(interrupt),然后某男去拉屎去了。最終線程退出了運行,在這里你會發現,park并不會拋出InterruptedException異常。那問題來,不拋出異常,那和正常的unpark有何區別?不能拉屎中斷和女朋友召喚效果一樣吧。這里就要依賴線程的interruptedstatus,如果線程被中斷而退出阻塞該狀態會被修改為true。如下可獲取到當前interrupted status。
總結起來LockSupport有以下不同和特點:
其實現機制和wait/notify有所不同,面向的是線程。
不需要依賴監視器
與wait/notify沒有交集
使用起來更加靈活方便
總結
以上是生活随笔為你收集整理的java并发包系列---LockSupport的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HBase之BloomFilter
- 下一篇: HBase上关于CMS、GC碎片、大缓存