Java精通並發-鎖粗化與鎖消除技術實例演示與分析


在上一次https://www.cnblogs.com/webor2006/p/11446473.html中對鎖的升級進行了一個比較詳細的理論化的學習,先回憶一下:

 

編譯器對於鎖的優化措施: 

鎖消除技術:

接下來則會通過實例來分析一下JIT編譯器優化的一些方式,先來看第一個例子:

 

很簡單的程序,然后反編譯看一下它在字節碼的表現:

 

 接下來則來修改一下程序:

其實反編譯的字節碼的鎖還是會有的:

但是很明顯這段同步的意義就不大了,因為每個線程在訪問這個方法時的局部變量肯定都是不一樣的,不同的對象鎖也不一樣,那何來的同步,所以其實JIT在程序運行時是比較智能的,JIT編譯器(Just In Time編譯器)可以在動態編譯同步代碼時,使用一種叫做逃逸分析的技術,來通過該項技術判別程序中所使用的鎖對象是否只被一個線程所使用,而沒有散布到其他線程當中;如果情況就是這樣的話,那么JIT編譯器在編譯這個同步代碼時就不會生成synchronized關鍵字標識的鎖的申請和釋放機器碼,從而消除了鎖的使用流程。

鎖粗化:

好,接下來看另外一個例子:

 

根據上面的理論,很顯然在運行是JIT是不會給代碼上鎖的,因為此object聲明的是方法的局部變量,木啥意義,那如果將它改為成員變量呢?

 

可見這個方法塊中多次給代碼上了鎖,下面看一下它在字節碼上的表現:

  public void method();
    Code:
       0: aload_0
       1: getfield      #3                  // Field object:Ljava/lang/Object;
       4: dup
       5: astore_1
       6: monitorenter 7: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      10: ldc           #5                  // String hello world
      12: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      15: aload_1
      16: monitorexit 17: goto          25
      20: astore_2
      21: aload_1
      22: monitorexit 23: aload_2
      24: athrow
      25: aload_0
      26: getfield      #3                  // Field object:Ljava/lang/Object;
      29: dup
      30: astore_1
      31: monitorenter 32: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      35: ldc           #7                  // String welcome
      37: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      40: aload_1
      41: monitorexit 42: goto          50
      45: astore_3
      46: aload_1
      47: monitorexit 48: aload_3
      49: athrow
      50: aload_0
      51: getfield      #3                  // Field object:Ljava/lang/Object;
      54: dup
      55: astore_1
      56: monitorenter 57: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      60: ldc           #8                  // String person
      62: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      65: aload_1
      66: monitorexit 67: goto          77
      70: astore        4
      72: aload_1
      73: monitorexit 74: aload         4
      76: athrow
      77: return
    Exception table:
       from    to  target type
           7    17    20   any
          20    23    20   any
          32    42    45   any
          45    48    45   any
          57    67    70   any
          70    74    70   any

每一個synchronized塊都對應一個monitorenter和兩個monitorexit,其實JIT編譯器在執行動態編譯時會對上面代碼進行優化:若發現前后相鄰的synchronized塊使用的是同一個鎖對象,那么它就會把這幾個synchronized塊給合並為一個較大的同步塊,這樣做的好處在於線程在執行這些代碼時,就無需頻繁申請與釋放鎖了,從而達到申請與釋放鎖一次,就可以執行完全部的同步代碼塊,從而提升了性能。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM