從新認識synchronized(下)


synchronized既保證原子性,又保證內存可見性,是一種線程同步的方式,是鎖機制的一種java實現。synchronized的實現基於JVM底層,JVM是基於monitor實現的,而monitor的實現依賴於操做系統的互斥實現。
html

語義

synchronized語義是同步,但同步有兩層含義:java

  1. 互斥,即鎖的特色。同一時間只能有一個線程持有監視器,所以一旦線程進入監視器保護的代碼塊(即臨界區),其餘線程是不容許監視器保護的代碼塊,直到前一個線程退出代碼塊。互斥阻止了其餘線程看到對象不一致的狀態,與原子性有相同的語義。
  2. 可見。synchronized保證進入同步代碼塊的線程,均可以看到由同一個鎖保護的以前全部的修改效果。緣由是:在釋放監視器時(即退出同步代碼塊),會將工做內存中未映射到主內存的工做拷貝,強制刷新回主內容。在獲取監視器是(即進入同步代碼塊時),監視器會使本地內存失效,強制從主內存拷貝到工做內存。

互斥保證在線程退出前,全部對象狀態變動都對其餘線程不可見;可見保證在線程進入同步代碼塊時,能夠看到上一個線程對對象狀態變動的最終狀態。編程

線程安全與同步

線程安全代表在多線程環境中,不會有多個線程同時訪問共享數據。
線程同步是線程訪問類和實例字段變量,和其餘共享資源的一種串行化行爲,確保在同一時間只能有一個線程訪問資源。舉個栗子,春運火車票只剩下最後一張火車票,A,B都要搶這張火車票,怎麼解決這個問題防止超賣呢?把資源保護起來,讓A,B排隊來買火車票。
線程安全是屬性,線程同步是方式。安全

指令

synchronized同步代碼塊是經過monitorenter和monitorexit指令實現的,而synchronized同步方法是基於ACC_SYCHRONIZED標誌,同步方法被調用時JVM會檢查這個標誌。monitorenter標記臨界區的開始,線程執行到 monitorenter 指令時,將會嘗試獲取對象所對應的 monitor 的全部權;monitorexit標記臨界區的結束,線程執行到 monitorexit 指令時,將釋放對象所對應的 monitor 的全部權。多線程

1 public class SynchronizedMethod {
2     public synchronized void methodA() {
3         System.out.println("MethodA start");
4 
5     }
6 }

將這段代碼經過 javap -c 反編譯一下,重點關注一下編譯後的第3行和第13行。併發

 1 Compiled from "SynchronizedTest.java"
 2 public class com.memory.SynchronizedTest {
 3   public com.memory.SynchronizedTest();
 4     Code:
 5        0: aload_0       
 6        1: invokespecial #1                  // Method java/lang/Object."<init>":()V
 7        4: return        
 8 
 9   public void methodA();
10     Code:
11        0: aload_0       
12        1: dup           
13        2: astore_1      
14        3: monitorenter  
15        4: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
16        7: ldc           #3                  // String MethodA start
17        9: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
18       12: aload_1       
19       13: monitorexit   
20       14: goto          22
21       17: astore_2      
22       18: aload_1       
23       19: monitorexit   
24       20: aload_2       
25       21: athrow        
26       22: return        
27     Exception table:
28        from    to  target type
29            4    14    17   any
30           17    20    17   any
31 }

鎖的種類oracle

java synchronized鎖升級java synchronized鎖升級
JDK1.6中對synchronized優化引入了偏向鎖,輕量級鎖,重量級鎖。鎖的升級過程是單方向的,只容許從低到高升級,不容許降級。
oop

重量級鎖(Heavyweight Lock)是將程序運行交出控制權,將線程掛起,由操做系統來負責線程間的調度,負責線程的阻塞和執行。這樣會出現頻繁地對線程運行狀態的切換,線程的掛起和喚醒,消耗大量的系統資源,致使性能低下。post

輕量級鎖(lightweight Locking)是相對於重量級鎖而言的,在synchronized實現中使用自旋的方式,實際是經過CPU自旋等待的方式替代線程切換,競爭的線程不會所以而阻塞,避免阻塞喚醒形成的CPU負荷。採用自旋的方式有利有弊,當鎖佔用的時間較短時,較少次數的自旋等待就能夠獲取鎖;但在鎖佔用的時間較長時,自旋會白白浪費大量的CPU資源。所以自旋的次數有必定要在限定以內,自旋失敗就會當即將鎖升級爲重量級鎖,稱爲鎖膨脹。性能

偏向鎖(Biased Locking )從字面含義是這把鎖是有私心的,會傾向於上次訪問的線程。Hotspot的做者在他的論文《QRL-OpLocks-BiasedLocking》中闡述到,研究發現大多數狀況下不存在多線程爭奪共享資源,並且老是由同一線程屢次得到,考慮到CAS (Compare-And-Swap)指令在獲取Java監視器時會形成較大的CPU延遲,爲了讓線程得到鎖的代價更低而引入了偏向鎖。

鎖標記的位置

64位虛擬機中,標記字段(Mark Word)中包含哈希嗎(HashCode,存放31bits對象的hashcode值),GC分代年齡(Generational GC Age,4bits,所以分代年齡最高爲15),偏向線程ID,偏向鎖標記。
synchronized鎖的四個狀態:無鎖狀態,偏向鎖,輕量級鎖和重量級鎖,在Mark Word中對應不一樣的字段。
java synchronized不一樣級別鎖中的Mark Wordjava synchronized不一樣級別鎖中的Mark Word

我是葛一凡,但願對你有用。

參考

  1. JSR 133 (Java Memory Model) FAQ
  2. Java內存模型(一)
  3. Java 鎖機機制——淺析 Synchronized
  4. Biased Locking in HotSpot
  5. Java Synchronised機制
  6. 深刻JVM鎖機制1-synchronized
  7. 多線程之:偏向鎖,輕量級鎖,重量級鎖
  8. JVM內部細節之一:synchronized關鍵字及實現細節(輕量級鎖Lightweight Locking)
  9. java鎖優化
  10. Java併發編程:Synchronized底層優化(偏向鎖、輕量級鎖)
  11. 虛擬機中的鎖優化簡介(適應性自旋/鎖粗化/鎖削除/輕量級鎖/偏向鎖)
  12. JVM內部細節之二:偏向鎖(Biased Locking)
  13. 小議偏向鎖
  14. synchronized、鎖、多線程同步的原理是咋樣的
  15. Java中的鎖
  16. markOop.hpp
  17. 5 Things You Didn’t Know About Synchronization in Java and Scala
  18. 論文:The Smart Energy Management of Multithreaded Java Applications on Multi-Core Processors
相關文章
相關標籤/搜索