synchronized底層實現總結

看了synchronzied的底層實現的原理,在JVM中,鎖有專門的名字,對象監視器。當多個線程來請求同一個對象監視器時,這時候synchronized就起作用了。

線程請求的流程

首先請求獲得鎖的線程會先進入到Contention列表中,再從列表選中一些線程進入到EntryList中,再從EntryList中選擇一個線程成爲OnDeck(同一時間只有一個Ondeck),然後再執行(Owner狀態),執行完釋放鎖(!Owner狀態),然後再從Entry中選擇一個線程進入OnDeck狀態

Contention

FREE-LOCK結構,其並不是一個真正的隊列,而是鏈表結構,新進來的爲頭節點(CAS操作),進入EntryList的爲未節點,每次出列都是由Owner進行調度的

EntryList

作用是:ContentionList會被線程併發訪問,爲了降低對 ContentionList隊尾的爭用,而建立EntryList。從ContentionList中進來的方式跟進Contention一樣,一般Owner運行完(既unlock)之後會從EntryList中選擇一個線程作爲OnDeck(一般是頭節點),這叫‘競爭切換’。Owner進入wait()進入WaitSet隊列,notify之後會再次進入到EntryList中。

自旋鎖

線程被阻塞後便進入內核(Linux)調度狀態,這個會導致系統在用戶態與內核態之間來回切換,嚴重影響 鎖的性能,所以自旋就是每次爭用失敗後先等一等,實在爭不到再進入阻塞狀態,而java synchronized實現自旋的方式便是:在進入ContentionList之前先去嘗試獲得鎖,並自旋,沒有成功才進入隊列(Contention、Entry、WaitSet裏面的線程都是阻塞狀態的)。所以這樣對進入到隊列的線程是不公平的,而且獲得鎖還會導致OnDeck狀態的線程無法進入Owner,也是不公平的

偏向鎖

用於無競爭的情況下,由於加鎖放鎖會造成額外的系統消耗,所以偏向鎖會有一個指針ID,標記偏向的線程,下次還是這個ID的線程則直接進入,若不是這個ID的就更改這個指針ID標誌,如有兩個線程競爭了,則膨脹爲輕量級鎖

輕量級鎖

入鎖

首先查看兩個標誌位(鎖標誌位爲01,偏向鎖標誌位00)才能進行獲取,否則可能鎖標誌位爲00(代表有線程獲取了該鎖,也可能是自己獲取了)。首先在線程的堆棧中開闢一塊Lock record區域,拷貝Mark Word到線程的Lock record區域中,基於CAS將所對象的指針指向Lock record和將線程的owner指針指向鎖對象,此操作如果成果,代表獲得鎖成功,鎖標誌位置爲00,不成功則查看鎖對象的指針是否指向自己,是的話重入執行,不是的話代表有競爭,所標誌置爲10,然後膨脹爲重量級鎖。再後面的線程則阻塞,而本線程自旋,嘗試獲取鎖。

放鎖

將自己的Lock record的內容基於CAS替換鎖對象的Mask word,成功則完成同步,否則則是已經升級爲重量級鎖,釋放鎖的同時,喚醒掛起的線程。

總結

synchronized原理便是,JVM中有對象監視器,線程需要取得對象監視器才能進行操作,調用的機制便是自旋後阻塞,競爭切換後繼續競爭鎖。不公平,但是保證了吞吐量

 

參考:https://blog.csdn.net/tingfeng96/article/details/52219649

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