java 面試知識點筆記(十一)多線程與併發-原理 中上篇

自適應自旋鎖:(java6引入,jvm對鎖的預測會愈來愈精準,jvm也會愈來愈聰明)java

  1. 自選次數再也不固定
  2. 由前一次在同一個鎖上的自旋時間及鎖擁有者的狀態來決定(若是在同一個鎖對象上自旋等待剛剛成功獲取過鎖而且持有鎖的線程正在運行中,jvm會認爲該鎖自旋獲取到鎖的可能性很大,會自動增長等待時間,相反jvm 若是可能性很小會省掉自旋過程,避免浪費)

鎖消除:jvm的另外一種鎖優化,更完全的優化多線程

  • JIT編譯時,對運行上下文進行掃描,去除不可能存在的競爭的鎖,消除毫無心義的鎖

鎖粗化:另外一種極端,鎖消除的做用在儘可能小的範圍使用鎖,而鎖粗化則相反,擴大加鎖範圍。好比加鎖出如今循環體中,每次循環都要執行加鎖解鎖的,如此頻繁操做比較消耗性能jvm

  • 擴大加鎖範圍,避免反覆的加鎖和解鎖

synchronized的四種狀態性能

  1. 無鎖
  2. 偏向鎖
  3. 輕量級鎖
  4. 重量級鎖

鎖膨脹方向:無鎖->偏向鎖->輕量級鎖->重量級鎖,synchronized會隨着競爭狀況逐漸升級,如出現了閒置的monitor也會出現鎖降級優化

偏向鎖:減小同一個線程獲取鎖的代價.net

  1. 大多數狀況下,鎖不存在多線程競爭,老是由同一個線程屢次得到

ps:核心思想就是若是一個線程得到了鎖,那麼鎖就進入偏向模式,此時MarkWord的結構也變成偏向鎖結構,當該線程再次請求鎖時,無需再作任何同步操做,即獲取鎖的過程只須要檢查MarkWord的鎖標記位爲偏向鎖以及當前線程ID等於MarkWord的ThreadID便可,這樣就省去了大量有關鎖申請的操做線程

不適合用於鎖競爭比較激烈的多線程場合指針

輕量級鎖:對象

輕量級鎖是由偏向鎖升級而來的,偏向鎖運行再一個線程進入同步塊的狀況下,當第二個線程加入鎖爭用的時候,偏向鎖就會升級爲輕量級鎖blog

適用場景:線程交替執行的同步塊

若存在同一時間訪問同一鎖的狀況,就會致使輕量級鎖膨脹爲重量級鎖

輕量級鎖的加鎖過程:

此圖來自https://blog.csdn.net/zqz_zqz

  1. 在代碼進入同步塊的時候,若是同步對象鎖狀態爲無鎖狀態(鎖標誌位爲「01」狀態,是否爲偏向鎖爲「0」),虛擬機首先將在當前線程的棧幀中創建一個名爲鎖記錄(Lock Record)的空間(線程私有的棧幀裏),用於存儲鎖對象目前的Mark Word的拷貝(對象是存在堆中的,因此對象的MarkWord也再堆中),官方稱之爲 Displaced Mark Word。
  2. 拷貝對象頭中的Mark Word複製到鎖記錄中;
  3. 拷貝成功後,虛擬機將使用CAS操做嘗試將對象的Mark Word更新爲指向Lock Record的指針,並將Lock record裏的owner指針指向object mark word。若是更新成功,則執行步驟4,不然執行步驟5。
  4. 若是這個更新動做成功了,那麼這個線程就擁有了該對象的鎖,而且對象Mark Word的鎖標誌位設置爲「00」,即表示此對象處於輕量級鎖定狀態,這時候線程堆棧與對象頭的狀態如圖所示。
  5. 若是這個更新操做失敗了,虛擬機首先會檢查對象的Mark Word是否指向當前線程的棧幀,若是是就說明當前線程已經擁有了這個對象的鎖,那就能夠直接進入同步塊繼續執行。不然說明多個線程競爭鎖,輕量級鎖就要膨脹爲重量級鎖,鎖標誌的狀態值變爲「10」,Mark Word中存儲的就是指向重量級鎖(互斥量)的指針,後面等待鎖的線程也要進入阻塞狀態。 而當前線程便嘗試使用自旋來獲取鎖,自旋就是爲了避免讓線程阻塞,而採用循環去獲取鎖的過程。 

鎖的內存語義

  • 當線程釋放鎖時,java內存模型會把該線程對應的本地內存中的共享變量刷新到主內存中
  • 當線程獲取鎖時,java內存模型會把該線程對應的本地內存置爲無效,從而使得被監視器保護的臨界區代碼必須從主內存中讀取共享變量

總結:

相關文章
相關標籤/搜索