1. 程式人生 > 實用技巧 >Linux系統IO基準測試方法

Linux系統IO基準測試方法

synchronized程式碼塊底層原理

現在我們重新定義一個synchronized修飾的同步程式碼塊,在程式碼塊中操作共享變數i,如下

public class SyncCodeBlock {

   public int i;

   public void syncTask(){
       //同步程式碼塊
       synchronized (this){
           i++;
       }
   }
}

編譯上述程式碼並使用javap反編譯後得到位元組碼如下:

Classfile /Users/zejian/Downloads/Java8_Action/src/main/java/com/zejian/concurrencys/SyncCodeBlock.class
Last modified 2017-6-2; size 426 bytes MD5 checksum c80bc322c87b312de760942820b4fed5 Compiled from "SyncCodeBlock.java" public class com.zejian.concurrencys.SyncCodeBlock minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: //........省略常量池中資料 //建構函式 public com.zejian.concurrencys.SyncCodeBlock(); descriptor: ()V flags: ACC_PUBLIC Code: stack
=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 7: 0 //===========主要看看syncTask方法實現================ public void syncTask(); descriptor: ()V flags: ACC_PUBLIC Code: stack
=3, locals=3, args_size=1 0: aload_0 1: dup 2: astore_1 3: monitorenter //進入同步方法 4: aload_0 5: dup 6: getfield 9: iconst_1 10: iadd 11: putfield 14: aload_1 15: monitorexit //退出同步方法 16: goto 24 19: astore_2 20: aload_1 21: monitorexit //退出同步方法 22: aload_2 23: athrow 24: return Exception table: //省略其他位元組碼....... } SourceFile: "SyncCodeBlock.java"

從位元組碼中可知同步語句塊的實現使用的是monitorenter 和 monitorexit 指令,其中monitorenter指令指向同步程式碼塊的開始位置,monitorexit指令則指明同步程式碼塊的結束位置,當執行monitorenter指令時,當前執行緒將試圖獲取 objectref(即物件鎖) 所對應的 monitor 的持有權,當 objectref 的 monitor 的進入計數器為 0,那執行緒可以成功取得 monitor,並將計數器值設定為 1,取鎖成功。如果當前執行緒已經擁有 objectref 的 monitor 的持有權,那它可以重入這個 monitor (關於重入性稍後會分析),重入時計數器的值也會加 1。倘若其他執行緒已經擁有 objectref 的 monitor 的所有權,那當前執行緒將被阻塞,直到正在執行執行緒執行完畢,即monitorexit指令被執行,執行執行緒將釋放 monitor(鎖)並設定計數器值為0 ,其他執行緒將有機會持有 monitor 。值得注意的是編譯器將會確保無論方法通過何種方式完成,方法中呼叫過的每條 monitorenter 指令都有執行其對應 monitorexit 指令,而無論這個方法是正常結束還是異常結束。為了保證在方法異常完成時 monitorenter 和 monitorexit 指令依然可以正確配對執行,編譯器會自動產生一個異常處理器,這個異常處理器宣告可處理所有的異常,它的目的就是用來執行 monitorexit 指令。從位元組碼中也可以看出多了一個monitorexit指令,它就是異常結束時被執行的釋放monitor 的指令。

synchronized方法底層原理

方法級的同步是隱式,即無需通過位元組碼指令來控制的,它實現在方法呼叫和返回操作之中。JVM可以從方法常量池中的方法表結構(method_info Structure) 中的 ACC_SYNCHRONIZED 訪問標誌區分一個方法是否同步方法。當方法呼叫時,呼叫指令將會 檢查方法的 ACC_SYNCHRONIZED 訪問標誌是否被設定,如果設定了,執行執行緒將先持有monitor(虛擬機器規範中用的是管程一詞), 然後再執行方法,最後再方法完成(無論是正常完成還是非正常完成)時釋放monitor。在方法執行期間,執行執行緒持有了monitor,其他任何執行緒都無法再獲得同一個monitor。如果一個同步方法執行期間拋 出了異常,並且在方法內部無法處理此異常,那這個同步方法所持有的monitor將在異常拋到同步方法之外時自動釋放。下面我們看看位元組碼層面如何實現:

public class SyncMethod {

   public int i;

   public synchronized void syncTask(){
           i++;
   }
}

使用javap反編譯後的位元組碼如下:

Classfile /Users/zejian/Downloads/Java8_Action/src/main/java/com/zejian/concurrencys/SyncMethod.class
  Last modified 2017-6-2; size 308 bytes
  MD5 checksum f34075a8c059ea65e4cc2fa610e0cd94
  Compiled from "SyncMethod.java"
public class com.zejian.concurrencys.SyncMethod
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool;

   ...//==================syncTask方法======================
  public synchronized void syncTask();
    descriptor: ()V
    //方法標識ACC_PUBLIC代表public修飾,ACC_SYNCHRONIZED指明該方法為同步方法
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=3, locals=1, args_size=1
         0: aload_0
         1: dup
         2: getfield      #2                  // Field i:I
         5: iconst_1
         6: iadd
         7: putfield      #2                  // Field i:I
        10: return
      LineNumberTable:
        line 12: 0
        line 13: 10
}
SourceFile: "SyncMethod.java"

從位元組碼中可以看出,synchronized修飾的方法並沒有monitorenter指令和monitorexit指令,取得代之的確實是ACC_SYNCHRONIZED標識,該標識指明瞭該方法是一個同步方法,JVM通過該ACC_SYNCHRONIZED訪問標誌來辨別一個方法是否宣告為同步方法,從而執行相應的同步呼叫。這便是synchronized鎖在同步程式碼塊和同步方法上實現的基本原理。

在Java早期版本中,synchronized屬於重量級鎖,效率低下,因為監視器鎖(monitor)是依賴於底層的作業系統的Mutex Lock來實現的,而作業系統實現執行緒之間的切換時需要從使用者態轉換到核心態,這個狀態之間的轉換需要相對比較長的時間,時間成本相對較高,這也是為什麼早期的synchronized效率低的原因。在Java 6之後Java官方對從JVM層面對synchronized較大優化,所以現在的synchronized鎖效率也優化得很不錯了,Java 6之後,為了減少獲得鎖和釋放鎖所帶來的效能消耗,引入了輕量級鎖和偏向鎖。

--部分內容摘自https://blog.csdn.net/javazejian/article/details/72828483