java併發編程----synchronized 實現原理(偏向鎖,輕量級鎖,自旋鎖)

參考文章:http://blog.sina.com.cn/s/blog_c038e9930102v2hu.html    http://blog.sina.com.cn/s/blog_c038e9930102v2hu.html

參考書籍:Java併發編程的藝術

synchronized鎖信息存在Make Word 存儲對象是hashCode

java 1.6後 爲了synchronized減少獲取鎖和釋放鎖所帶來的性能的消耗,而引入  偏向鎖,輕量級鎖

一 偏向鎖:

當一個鎖總是同一個線程獲得,爲了讓線程獲得鎖的代價更低而引入了偏向鎖。當一個線程訪問同步代碼快方法時,會在對象頭和棧幀中的鎖信息裏存儲鎖偏向的線程ID,當下次進入退出同步代碼塊時,只需去對象頭的Makr Word判斷一下是否有鎖信息的偏向鎖指向該線程ID

偏向鎖的撤銷:

當有另一個線程來競爭鎖的時候,就不能再使用偏向鎖了,要膨脹爲輕量級鎖。

競爭線程嘗試CAS更新對象頭失敗,會等待到全局安全點(此時不會執行任何代碼)撤銷偏向鎖。

二 輕量級鎖:

a線程獲得鎖,會在a線程的棧幀裏創建lock record(鎖記錄變量),讓lock record的指針指向鎖對象的對象頭中的Makr Word.再讓Makr Word 指向lock record.這就是獲取了鎖,並且會複製一份Makr Word到lock record裏。

爲什麼要拷貝mark word?其實很簡單,原因是爲了不想在lock與unlock這種底層操作上再加同步。釋放鎖的時候會讓拷貝的displaced mark word     mark word 作比較。

--->輕量級鎖,b線程在鎖競爭時,發現鎖已經被a線程佔用,則b線程不進入內核態,讓b線程自旋,執行空循環,等待a線程釋放鎖。如果,完成自旋策略還是發現a線程沒有釋放鎖,或者讓c線程佔用了。則b線程試圖將輕量級鎖升級爲重量級鎖。

a線程解鎖後,我們發現,JVM同樣使用了CAS來驗證mark word在持有鎖到釋放鎖之間,有無被其他線程訪問。

如果其他線程在持有鎖這段時間裏,嘗試獲取過鎖,則可能自身被掛起,而mark word的重量級鎖指針也會被相應修改

釋放鎖線程視角:所以由輕量鎖切換到重量鎖,是發生在輕量鎖釋放鎖的期間,之前在獲取鎖的時候它拷貝了鎖對象頭的mark word,在釋放鎖的時候如果它發現在它持有鎖的期間有其他線程來嘗試獲取鎖了,並且該線程對mark word做了修改,兩者比對發現不一致,則切換到重量鎖。

因爲重量級鎖被修改了,所有displaced mark word和原來的mark word不一樣了。

如果此時b線程還在自旋狀態,a線程釋放了鎖,b線程自旋完成後,可以獲取到鎖,那就不會向重量鎖升級

JavaéSynchronizedä¹è½»éé

三 自旋鎖:

補充一點可能有一些剛入門的小夥伴還不知道自旋鎖是什麼,這裏先知道個概念,下次再來從源碼分析:

摘錄一個博客文章:

阻塞操作由操作系統完成(在Linxu下通過pthread_mutex_lock函數)

線程被阻塞後便進入內核(Linux)調度狀態,這個會導致系統在用戶態內核態之間來回切換,嚴重影響鎖的性能。----輕量級鎖時,競爭鎖時,不輕易阻塞當前線程。

緩解上述問題的辦法便是自旋。

synchronized實現何時使用了自旋鎖?答案是在線程進入ContentionList時,也即第一步操作前。線程在進入等待隊列時首先進行自旋嘗試獲得鎖,如果不成功再進入等待隊列。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章