被問到傻傻不懂synchronized底層原理

0x01:synchronized的基本語法

修飾實例方法,作用於當前實例加鎖,進入同步代碼前要獲得當前實例的鎖靜態方法,作用於當前類對象加鎖,進入同步代碼前要獲得當前類對象的鎖修飾代碼塊,指定加鎖對象,對給定對象加鎖,進入同步代碼庫前要獲得給定對象的鎖。

被問到傻傻不懂synchronized底層原理


0x02:代碼分析synchronized

<code>package com.lesson8;

public class SynchronizedDemo {

    public Object lock = new Object();

    public synchronized void  syncCommonMethod(){
        System.out.println("==syncCommonMethod==");
    }

    public static synchronized void  syncStaticMethod(){
        System.out.println("==syncStaticMethod==");
    }

    public void  syncBlockCode(){
        synchronized (lock) {
            System.out.println("==syncBlockCode==");
        }
    }

}/<code>

代碼中包三個方法,分別是synchronized修飾實例方法、synchronized靜態方法synchronized修飾代碼塊。使用命令:

javap -v SynchronizedDemo.class

被問到傻傻不懂synchronized底層原理

得到如下彙編指令:

<code>Classfile /D:/jmeterws/xml/com-lesson8/target/classes/com/lesson8/SynchronizedDemo.class
  Last modified 2020-4-5; size 926 bytes
  MD5 checksum f23cff99c70b8a401db1cc3bd74538ec
  Compiled from "SynchronizedDemo.java"
public class com.lesson8.SynchronizedDemo
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Class              #2             // com/lesson8/SynchronizedDemo
   #2 = Utf8               com/lesson8/SynchronizedDemo
   #3 = Class              #4             // java/lang/Object
   #4 = Utf8               java/lang/Object
   #5 = Utf8               lock
   #6 = Utf8               Ljava/lang/Object;
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Methodref          #3.#11         // java/lang/Object."<init>":()V
  #11 = NameAndType        #7:#8          // "<init>":()V
  #12 = Fieldref           #1.#13         // com/lesson8/SynchronizedDemo.lock:Ljava/lang/Object;
  #13 = NameAndType        #5:#6          // lock:Ljava/lang/Object;
  #14 = Utf8               LineNumberTable
  #15 = Utf8               LocalVariableTable
  #16 = Utf8               this
  #17 = Utf8               Lcom/lesson8/SynchronizedDemo;
  #18 = Utf8               syncCommonMethod
  #19 = Fieldref           #20.#22        // java/lang/System.out:Ljava/io/PrintStream;
  #20 = Class              #21            // java/lang/System
  #21 = Utf8               java/lang/System
  #22 = NameAndType        #23:#24        // out:Ljava/io/PrintStream;
  #23 = Utf8               out
  #24 = Utf8               Ljava/io/PrintStream;
  #25 = String             #26            // ==syncCommonMethod==
  #26 = Utf8               ==syncCommonMethod==
  #27 = Methodref          #28.#30        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #28 = Class              #29            // java/io/PrintStream
  #29 = Utf8               java/io/PrintStream
  #30 = NameAndType        #31:#32        // println:(Ljava/lang/String;)V
  #31 = Utf8               println
  #32 = Utf8               (Ljava/lang/String;)V
  #33 = Utf8               syncStaticMethod
  #34 = String             #35            // ==syncStaticMethod==
  #35 = Utf8               ==syncStaticMethod==
  #36 = Utf8               syncBlockCode
  #37 = String             #38            // ==syncBlockCode==

  #38 = Utf8               ==syncBlockCode==
  #39 = Utf8               StackMapTable
  #40 = Class              #41            // java/lang/Throwable
  #41 = Utf8               java/lang/Throwable
  #42 = Utf8               SourceFile
  #43 = Utf8               SynchronizedDemo.java
{
  public java.lang.Object lock;
    descriptor: Ljava/lang/Object;
    flags: ACC_PUBLIC

  public com.lesson8.SynchronizedDemo();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=1, args_size=1
         0: aload_0
         1: invokespecial #10                 // Method java/lang/Object."<init>":()V
         4: aload_0
         5: new           #3                  // class java/lang/Object
         8: dup
         9: invokespecial #10                 // Method java/lang/Object."<init>":()V
        12: putfield      #12                 // Field lock:Ljava/lang/Object;
        15: return
      LineNumberTable:
        line 3: 0
        line 5: 4
        line 3: 15
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      16     0  this   Lcom/lesson8/SynchronizedDemo;

  public synchronized void syncCommonMethod();
    descriptor: ()V
    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #19                 // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #25                 // String ==syncCommonMethod==
         5: invokevirtual #27                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 8: 0
        line 9: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  this   Lcom/lesson8/SynchronizedDemo;

  public static synchronized void syncStaticMethod();
    descriptor: ()V

    flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
    Code:
      stack=2, locals=0, args_size=0
         0: getstatic     #19                 // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #34                 // String ==syncStaticMethod==
         5: invokevirtual #27                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: return
      LineNumberTable:
        line 12: 0
        line 13: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature

  public void syncBlockCode();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=1
         0: aload_0
         1: getfield      #12                 // Field lock:Ljava/lang/Object;
         4: dup
         5: astore_1
         6: monitorenter
         7: getstatic     #19                 // Field java/lang/System.out:Ljava/io/PrintStream;
        10: ldc           #37                 // String ==syncBlockCode==
        12: invokevirtual #27                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        15: aload_1
        16: monitorexit
        17: goto          23
        20: aload_1
        21: monitorexit
        22: athrow
        23: return
      Exception table:
         from    to  target type
             7    17    20   any
            20    22    20   any
      LineNumberTable:
        line 16: 0
        line 17: 7
        line 16: 15
        line 19: 23
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      24     0  this   Lcom/lesson8/SynchronizedDemo;
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 20
          locals = [ class com/lesson8/SynchronizedDemo, class java/lang/Object ]
          stack = [ class java/lang/Throwable ]

        frame_type = 250 /* chop */
          offset_delta = 2
}
SourceFile: "SynchronizedDemo.java"/<init>/<init>/<init>/<init>/<init>/<code>

分析如上代碼發現synchronized修飾實例方法、synchronized靜態方法synchronized修飾代碼塊不一樣,synchronized修飾實例方法和synchronized靜態方法一樣

synchronized修飾代碼塊

被問到傻傻不懂synchronized底層原理


涉及兩條指令:

  • monitorenter:每個對象有一個監視器鎖(monitor)。當monitor被佔用時就會處於鎖定狀態,線程執行monitorenter指令時嘗試獲取monitor的所有權,過程如下:

如果monitor的進入數為0,則該線程進入monitor,然後將進入數設置為1,該線程即為monitor的所有者。

如果線程已經佔有該monitor,只是重新進入,則進入monitor的進入數加1。

如果其他線程已經佔用了monitor,則該線程進入阻塞狀態,直到monitor的進入數為0,再重新嘗試獲取monitor的所有權。

  • monitorexit:執行monitorexit的線程必須是objectref所對應的monitor的所有者。

指令執行時,monitor的進入數減1,如果減1後進入數為0,那線程退出monitor,不再是這個monitor的所有者。其他被這個monitor阻塞的線程可以嘗試去獲取這個monitor 的所有權。

synchronized修飾實例方法

被問到傻傻不懂synchronized底層原理

synchronized靜態方法

被問到傻傻不懂synchronized底層原理

從反編譯的結果來看,方法的同步並沒有通過指令monitorenter和monitorexit來完成(理論上其實也可以通過這兩條指令來實現)。相對於普通方法,其常量池中多了ACC_SYNCHRONIZED標示符。

JVM就是根據該標示符來實現方法的同步的:當方法被調用時,調用指令將會檢查方法的 ACC_SYNCHRONIZED 訪問標誌是否被設置,如果設置了,執行線程將先獲取monitor,獲取成功之後才能執行方法體,方法執行完後再釋放monitor。在方法執行期間,其他任何線程都無法再獲得同一個monitor對象。其實本質上沒有區別,只是方法的同步是一種隱式的方式來實現,無需通過字節碼來完成。


分享到:


相關文章: