目前在Java語言層面能實現阻塞喚醒的方式一共有三種:suspend與resume組合、wait與notify組合、park與unpark組合。javascript
其中suspend與resume由於存在沒法解決的竟態問題而被Java廢棄,一樣,wait與notify也存在竟態條件,wait必須在notify以前執行,假如一個線程先執行notify再執行wait將可能致使一個線程永遠阻塞,如此一來,必需要提出另一種解決方案,就是park與unpark組合,它位於JDK的juc包下,應該也是由於當時編寫juc時發現java現有方式沒法解決問題而引入的新阻塞喚醒方式,因爲park與unpark使用的是許可機制,許可最大爲1,因此unpark與park操做不會累加,並且unpark能夠在park以前執行,如unpark先執行,後面park將不阻塞。java
Java真正意義上的語言層面上的併發編程應該從併發專家Doug Lea領導的JSR-166開始,此規範請求向JCP提交了向Java語言中添加併發編程工具,即在jdk中添加java.util.concurrent工具包供開發者使用,開發者能夠輕鬆構建本身的同步器,而在此以前併發過程當中同步都只能依靠JVM內置的管程。node
JDK的併發包中最重要的一個框架就是ASQ框架,它的阻塞和喚醒使用的是LockSupport類的park與unpark方法,分別調用的是Unsafe類的park與unpark本地方法。邏輯以下:編程
阻塞併發
if(嘗試獲取鎖失敗) {
建立node
使用CAS方式把node插入到隊列尾部
while(true){
if(嘗試獲取鎖成功 而且 node的前驅節點爲頭節點){
把當前節點設置爲頭節點
跳出循環
}else{
使用CAS方式修改node前驅節點的waitStatus標識爲signal
if(修改爲功)
LockSupport.park();
}
}
}複製代碼
喚醒框架
if(嘗試釋放鎖成功){
LockSupport.unpark(下一節點包含的線程);
}複製代碼
假如一條線程參與鎖競爭,首先先嚐試獲取鎖,失敗的話建立節點並插入隊列尾部,而後再次嘗試獲取鎖,如若成功則不作其餘任務處理直接返回,不然設置節點狀態爲待運行狀態,最後使用LockSupport的park阻塞當前線程。前驅節點運行完後將嘗試喚醒後繼節點,使用的便是LockSupport的unpark喚醒。工具
總的來講,JDK提供的併發工具,在阻塞與喚醒操做方面因爲suspend與resume存在各類各樣問題,必須使用LockSupport中提供的方法操做。post
相關閱讀:
從JDK源碼角度看併發競爭的超時
從 JDK 源碼角度看 Java 併發的公平性spa
歡迎關注:
線程