JAVA鎖介紹

以前的文章中介紹了JAVA中一些併發鎖使用方法以及裏面的介紹。同時以後還介紹了字節碼的操做碼,讓你們先了解下里面的指令,我這裏也是從表面中去講解下鎖底層操做碼的實現。java

鎖對象程序:

package com.montos.detail;
public class SynchronizedDemo {
	public static void main(String[] args) {
		SynchronizedDemo demo = new SynchronizedDemo();
		demo.demo();
	}
	public void demo() {
		synchronized (this) {
			System.out.println("this is demo");
		}
	}
}
複製代碼

對其反編譯:數組

public class com.montos.detail.SynchronizedDemo {
  public com.montos.detail.SynchronizedDemo();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class com/montos/detail/SynchronizedDemo
       3: dup
       4: invokespecial #3                  // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: invokevirtual #4                  // Method demo:()V
      12: return
  public void demo();
    Code:
       0: aload_0
       1: dup
       2: astore_1
       3: monitorenter
       4: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
       7: ldc           #6                  // String this is demo
       9: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      12: aload_1
      13: monitorexit
      14: goto          22
      17: astore_2
      18: aload_1
      19: monitorexit
      20: aload_2
      21: athrow
      22: return
    Exception table:
       from    to  target type
           4    14    17   any
          17    20    17   any
}

複製代碼

查看上面反編譯的結果,咱們能夠看到反編譯裏面是存在monitorenter以及monitorexit的操做碼,這兩個操做碼的做用就是:併發

  1. monitorenter:每一個對象都是一個監視器鎖(monitor)。當monitor被佔用時就會處於鎖定狀態,線程執行monitorenter指令時嘗試獲取monitor的全部權,過程以下:
  • 若是monitor的進入數爲0,則該線程進入monitor,而後將進入數設置爲1,該線程即爲monitor的全部者;
  • 若是線程已經佔有該monitor,只是從新進入,則進入monitor的進入數加1;
  • 若是其餘線程已經佔用了monitor,則該線程進入阻塞狀態,直到monitor的進入數爲0,再從新嘗試獲取monitor的全部權;
  1. monitorexit:執行monitorexit的線程必須是objectref所對應的monitor的全部者。指令執行時,monitor的進入數減1,若是減1後進入數爲0,那線程退出monitor,再也不是這個monitor的全部者。其餘被這個monitor阻塞的線程能夠嘗試去獲取這個 monitor 的全部權。

從而達到線程之間的串行執行,同時我能夠看到裏面有兩次monitorexit操做碼:第1次爲同步正常退出釋放鎖;第2次爲發生異步退出釋放鎖;這上面鎖住的就是this。異步

鎖方法程序:

public class SynchronizedDemo {
	public synchronized void method() {
		System.out.println("this is demo");
	}
}
複製代碼

反編譯:佈局

public com.montos.detail.SynchronizedDemo();
    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 3: 0
  public synchronized void method();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String this is demo
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 5: 0
        line 6: 8
複製代碼

經過上面反編譯,咱們發現沒有以前的兩個操做碼了,多出來的是有標識ACC_SYNCHRONIZED,這裏其實也是經過上面兩個操做碼完成的。這個方法也只是比普通的方法在常量池中多了ACC_SYNCHRONIZED字段。性能

當方法調用時,調用指令將會檢查方法的ACC_SYNCHRONIZED訪問標誌是否被設置,若是設置了,執行線程將先獲取monitor,獲取成功以後才能執行方法體,方法執行完後再釋放monitor。在方法執行期間,其餘任何線程都沒法再得到同一個monitor對象。this

上面的兩種操做本質上沒有區別,只是方法的同步是一種隱式方式操做的,兩個指令的執行是JVM經過調用操做系統的互斥原語mutex來實現,被阻塞的線程會被掛起、等待從新調度,會致使「用戶態和內核態」兩個態之間來回切換,對性能有較大影響。spa

鎖定關鍵點:

對象在內存中佈局主要有:對象頭,實例數據以及對齊填充。操作系統

  • 實例數據:存放類的屬性數據信息,包括父類的屬性信息;
  • 對齊填充:因爲虛擬機要求 對象起始地址必須是8字節的整數倍。填充數據不是必須存在的,僅僅是爲了字節對齊;
  • 對象頭:Java對象頭通常佔有2個機器碼(在32位虛擬機中,1個機器碼等於4字節,也就是32bit,在64位虛擬機中,1個機器碼是8個字節,也就是64bit),可是 若是對象是數組類型,則須要3個機器碼,由於JVM虛擬機能夠經過Java對象的元數據信息肯定Java對象的大小,可是沒法從數組的元數據來確認數組的大小,因此用一塊來記錄數組長度。

Synchronized用的鎖就是存在Java對象頭裏的,那麼什麼是Java對象頭呢?Hotspot虛擬機的對象頭主要包括兩部分數據:Mark Word(標記字段)、Class Pointer(類型指針)。其中 Class Pointer是對象指向它的類元數據的指針,虛擬機經過這個指針來肯定這個對象是哪一個類的實例,Mark Word用於存儲對象自身的運行時數據,它是實現輕量級鎖和偏向鎖的關鍵。線程

這裏面咱們主要注意的是Mark Word這個存儲結構。

每個Java對象建立出來就帶了一把看不見的鎖,它叫作內部鎖或者Monitor鎖Monitor對象存在於每一個Java對象的對象頭Mark Word中(存儲的指針的指向),Synchronized鎖即是經過這種方式獲取鎖的,也是爲何Java中任意對象能夠做爲鎖的緣由,同時notify/notifyAll/wait等方法會使用到Monitor鎖對象,因此必須在同步代碼塊中使用。

相關文章
相關標籤/搜索