synchronize原理分析

synchronized是什麼?

是一個關鍵字,可以修飾方法,又可以細分為類方法和實例方法。

雖然synchronized也能直接鎖定一個代碼塊,但本質上鎖定的無非還是類或實例

synchronized能解決什麼問題?

通俗的講, synchronized提供了鎖的能力。避免一個資源被多個線程同時調用導致的數據錯亂,可以通過該關鍵字來保證線程同步、安全。

Synchronized修飾不同對象的鎖粒度?

  • 修飾類方法,這裡類方法指static修飾的方法,鎖粒度為類的Class對象
  • 修飾實例方法,鎖的粒度為類對應的實例對象,即new出來的對象

Synchronized實現原理

同步方法和同步代碼塊的實現略微有些差異,但本質上沒啥區別。

同步方法,通過查看字節碼,可以看到通過synchronized修飾的放在在flag標誌為增加了一個ACC_SYNCHRONIZED標誌位,該標誌位告訴JVM,進入該方法前需要先獲取鎖,獲取鎖成功後,鎖計數+1,方法執行結束後,釋放鎖,鎖計數-1

同步代碼塊,通過查看字節碼,可以看到通過synchronized鎖定的代碼塊,在對應的指令前後增加了monitorenter和monitorexit兩個指令,在執行monitorenter指令前先要獲取鎖,獲取鎖成功,鎖計數+1, monitorexit執行執行過後會釋放鎖,鎖計數-1

synchronized鎖是可重入的,同一個線程再次申請鎖,鎖計數器持續+1

上面所說的獲取鎖,指的是每個對象都有的監視器鎖

通過下面圖片中的示例說明synchronized在字節碼層面是如何存在的,下圖的類中提供了4個方法

  1. test1(),synchronized修飾的實例方法
  2. test2(),synchronized修飾的類方法
  3. test3(),普通的類方法
  4. test4(),普通的實例方法,但方法內部包含synchronized同步塊


synchronize原理分析

實例代碼


synchronize原理分析

test1()方法的字節碼上flag中包含“ACC_SYNCHRONIZED”標誌位


synchronize原理分析

test2()方法的字節碼上flag中包含“ACC_SYNCHRONIZED”標誌位


synchronize原理分析

test3()方法的字節碼上沒有“ACC_SYNCHRONIZED”標誌位


synchronize原理分析

synchronized同步代碼塊的字節碼增加了monitorenter、monitorexit指令


synchronized鎖狀態

JDK1.6之前只有兩種狀態:無鎖狀態、有鎖狀態(重量級鎖)

JDK1.6之後又增加了兩種狀態:無鎖狀態、偏向鎖、輕量級鎖、重量級鎖

鎖的升級過程:偏向鎖->輕量級鎖->重量級鎖

這是官方對synchronized進行的一次優化,使得synchronized的性能大大提升

從鎖的各個狀態定義不難理解,首先是偏向鎖,大多的情況下是不會發生線程競爭的,當第一個線程獲得鎖之後,此時鎖的狀態變更是:無鎖->偏向鎖,當該線程再次嘗試獲取鎖時,因為是同一個線程,不需要再進行同步操作,直接就能獲取到鎖。如何做到同一個線程多次快速獲取到鎖?其關鍵點在於對象頭,當鎖變為偏向鎖的時候,對象頭中會記錄線程的ID,同一線程再次獲取鎖時,值需要對比下對象頭中記錄的鎖的線程Id是否一致即可完成。


synchronize原理分析

在升級輕量級鎖的過程中,有可能會失敗,而且很多情況下,線程持有鎖的時間極短,比線程獲得CPU調度等待的時間還短,這樣會導致性能不高,JVM為此做了自旋鎖的優化,通過自旋鎖讓升級輕量級鎖失敗的線程不讓出CPU,這樣當爭搶到鎖之後,立即就能執行,不需要再等待CPU調度了,但是自旋鎖如果遲遲爭搶不到鎖,會消耗CPU

其次是輕量級鎖,當有第二個線程獲取鎖,但與第一個線程並沒有出現競爭關係,兩個線程只是交替的執行,此時鎖就會升級為輕量級鎖,鎖狀態:偏向鎖->輕量級鎖

最後是重量級鎖,當同一時間有多個線程在獲取鎖,多個線程出現競爭,此時就會升級為重量級鎖,鎖狀態:輕量級->重量級

Synchronized其他優化

  1. 鎖消除,JVM或主動發現有一些鎖其實是不必要的,會在編譯階段消除synchronized
  2. 鎖粗化,JVM會對一些細粒度的鎖進行粗化以提高執行效率,避免反覆加鎖帶來的開銷。比如在一個循環內進行synchronized,JVM會將同步指令優化至循環外


synchronize原理分析


分享到:


相關文章: