前言:

最近重讀java並發編程實戰,又讀到了wait()和notify()必須在synchronized塊中使用這一段,當時沒有看懂,這次重新思考一下這個問題。

轉載:

首先貼上搜到的資料:

Java的LockSupport.park()實現分析

Java的wait()、notify()學習三部曲之一:JVM源碼分析

深入剖析基於並發AQS的(獨佔鎖)重入鎖(ReetrantLock)及其Condition實現原理 Java虛擬機對synchronized的優化

正文:

第一次思考這個問題的時候,覺得wait方法和synchronized是兩個毫不相關的東西,覺得synchronized是用來給代碼加鎖的,wait和notify是控制線程的執行暫停的,他們倆根本就是雷鋒和雷峯塔嘛。。。

這次看AQS代碼的時候,發現了park和unpark這兩個方法,再加上看了轉載的第一篇帖子Java的LockSupport.park()實現分析裡邊對兩者的描述,忽然明白了兩者的聯繫:

wait和synchronized 都是控制線程執行和暫停的方式,只是wait是我們主動控制線程暫停,synchronized 線程是被動暫停。為了保證資源的獨佔性,必須讓其他線程暫停。所以在適當的時候讓合適的線程暫停就能實現線程的同步。

讓線程暫停可以使用一個while(true)循環,在循環內部檢測某個條件,但是這樣的話會浪費大量的時間片執行空循環。所以操作系統提供了讓線程讓出時間片的系統調用,比如unix裏的pthread_join、pthread_exit以及semaphore和mutex。

java裡邊的park和unpark就類似於這樣的調用,可以讓程序控制線程的運行狀態。而synchronized是對這兩個方法更高級的抽象,不需要我們手動調用底層的方法就能實現同步,從轉載的第二篇文章Java的wait()、notify()學習三部曲之一:JVM源碼分析裏也能看到,synchronized最終讓線程阻塞就是調用的park方法,離開同步塊的時候調用的是unpark方法。

最後的wait和notify猜測的話,因為synchronized一旦持有了鎖,不能被中斷,如果出現死鎖就永遠解不開了,所以纔有了wait這種可以響應中斷的方法來豐富synchronized的功能。因為不好用,所以後來乾脆把park和unpark封裝成nativeAPI,在此基礎上封裝出了AQS,並用AQS實現了ReetrantLock,來代替synchronized。

在最後一篇轉載中提到java對synchronized做了很多優化,比如:偏向鎖、鎖消除等等,在第一篇中也有提到,大概意思是說如果直接調用park和unpark會發生系統調用,這些優化就是通過各種方式減少這兩個方法的調用。回想一下AQS代碼這麼複雜,核心的目的好像也是減少這兩個方法的調用次數(比如node的share模式對應偏向鎖)。

總結:

記得看其他人的文章,開頭或者結尾總是要把參考資料附在上邊。我思考了一下,覺得在網上發表文章,一種是作者高屋建瓴,能夠寫出特別專業的介紹和解讀,這種的文章覺得好,轉載就好了,自己寫的不如別人是因為自己還沒有到達那個水平,不用覺得受打擊。另一種文章,旁徵博引但是不知所云(比如我寫的這個)是因為菜鳥都有一個練習的過程,遇到這種的文章,也不用覺得煩,好好思考,能夠總結出自己的結論,因為別人看了我寫的東西,說不定也是一陣蛋疼[哭笑]。

最後,讓我們保持獨立思考,不卑不亢。長成自己想要的樣子!

(引用自 我非常喜歡的B站up主 」獨立菌兒「的口頭禪嗶哩嗶哩 ( ゜- ゜)つロ 乾杯~ Bilibili)
推薦閱讀:
相關文章