Java多線程synchronized關鍵字引出的多種鎖

前言

Java 中的 synchronized關鍵字能夠在多線程環境下用來做爲線程安全的同步鎖。本文不討論 synchronized 的具體使用,而是研究下synchronized底層的鎖機制,以及這些鎖分別的優缺點。安全

1、synchronized機制

synchronized關鍵字是JAVA中經常使用的同步功能,提供了簡單易用的鎖功能。 synchronized有三種用法,分別爲:性能優化

  • 用在普通方法上,可以鎖住當前對象。
  • 用在靜態方法上,可以鎖住類
  • 用在代碼塊上,鎖住的是synchronized()裏的對象

在JDK6以前,synchronized使用的是重量級鎖制,在以後synchronized加入了鎖膨脹機制,顯著提高了synchronized關鍵字的效率。多線程

基於synchronized關鍵字,咱們來了解下幾種類別的鎖,而且講解synchronized的鎖膨脹機制。性能

synchronized鎖是非公平鎖。而且一個被synchronized鎖住的對象或類,就是一把鎖。優化

另一提,全部鎖都是存儲在Java對象頭裏的,Java對象頭裏的Mark Word裏默認存儲對象的HashCode,分代年齡和鎖標記位。也就是說Mark Word記錄了鎖的狀態操作系統

2、鎖膨脹機制與幾類鎖

鎖膨脹是不可逆的線程

2.1 偏向鎖

synchronized在JDK1.6之後默認開啓偏向鎖synchronized最初都是偏向鎖code

表現:一個線程獲取鎖成功後,會在對象頭裏記錄線程ID,之後該線程獲取和釋放鎖都沒有任何花費。(由於該鎖已經被綁定在該線程上了,且在膨脹前不會改變),若是其餘線程嘗試獲取這個鎖,偏向鎖將會膨脹爲輕量鎖對象

優勢:在只有一個線程使用鎖的時候獲取和退出鎖沒有任何花費blog

缺點:鎖競爭激烈會很快升級爲輕量鎖,那麼維持偏向鎖的過程就是在浪費計算機資源。(不過由於偏向鎖自己就很輕量,所以浪費的資源並很少)

小結:只有一個線程使用鎖的狀況下,synchronized使用的鎖爲偏向鎖。 若是鎖競爭激烈,能夠經過配置JDK禁用偏向鎖

2.2 輕量鎖

一把鎖不止一個線程使用,則偏向鎖膨脹爲輕量鎖

表現:線程獲取輕量鎖時,會直接用CAS修改對象頭裏鎖的記錄,若是修改失敗,表明此時鎖存在多個線程的競爭,輕量鎖將會膨脹爲重量鎖

優勢:在線程之間使用鎖不存在競爭時,一次CAS操做就能獲取和退出鎖

缺點:與偏向鎖相似

小結:只要一把鎖不止一個線程獲取過,偏向鎖就會膨脹爲輕量鎖

2.3 重量鎖

一把鎖存在多線程競爭,則輕量鎖開始自旋,自旋必定次數後仍沒獲取鎖,則膨脹爲重量鎖(存在競爭時,輕量鎖雖然會先自旋,可是最終每每都會膨脹爲重量鎖)

表現:線程獲取重量鎖時,若是獲取失敗(即鎖已被其餘線程獲取),則使用自適應自旋鎖,自旋必定次數後仍沒獲取鎖,則進入阻塞隊列等待。

優勢:未獲取到的鎖進入阻塞隊列,節約CPU資源。(好吧感受實際上是沒有啥優勢)

缺點:重量鎖是經過對象內部的監視器(monitor)實現,其中monitor的本質是依賴於底層操做系統的Mutex Lock實現,操做系統實現線程之間的切換須要從用戶態到內核態的切換,切換成本很是高。

小結:只要一把鎖存在多線程競爭,輕量鎖就會膨脹爲重量鎖

自旋鎖

synchronized輕量鎖重量鎖,使用了自適應自旋鎖進行性能優化

首先介紹自旋鎖

表現:線程獲取鎖失敗後,不會進入阻塞等待,而是再次嘗試去獲取鎖,如此反覆,直到獲取到鎖,或者自旋結束那麼會阻塞等待。

解決問題:在某些場景下,線程持有鎖的時間很是短。在線程獲取鎖失敗後,若是線程進入阻塞將會帶來線程上下文的切換,上下文切換的時間可能反而高於線程反覆嘗試獲取鎖的時間。 此時線程原地等待去重複獲取鎖。反而在性能上更有優點。

缺點:

  1. 單核CPU沒有線程並行,反覆嘗試會致使進程沒法繼續運行。
  2. 重複嘗試致使了CPU的佔用,若是CPU資源緊張的話反而會性能降低
  3. 若是鎖的競爭時間過長,不只沒有性能提高,還浪費了大量CPU資源。

優化:使用自適應自旋鎖。自適應自旋鎖會根據以前的鎖獲取記錄,優化調整自旋時間,避免形成沒必要要的自旋。

3、具體synchronized流程

點關注,不迷路,這是一個程序都想要關注的公衆號

相關文章
相關標籤/搜索