Synchronized详解

本文详细解析了在Java中synchronized关键字的底层实现,通过实例和汇编指令探讨了synchronized如何触发monitorenter和monitorexit指令,以及为何同步代码会包含两次monitorexit。
摘要由CSDN通过智能技术生成

1:如果在普通方法上加上Synchronized 当前this锁。

2:如果在静态方法上加上Synchronized 当前calss字节码

3:也可以自定义锁对象

我们站在汇编的角度去分析Synchronized关键字原理

public class Demo2 extends Thread{
    //定义一个锁对象
    private Object lockObject = new Object();

    @Override
    public void run() {
         metho1();
    }

    private void metho1() {
             synchronized (lockObject){
                 System.out.println("我是metho1调用metho2");
                 metho2();
             }
    }

    private void metho2() {
        synchronized (lockObject){
            System.out.println("我是etho2");
        }
    }

    public static void main(String[] args) {


    }
}

我们把class文件转换成汇编指令:

 Last modified 2022-5-3; size 1138 bytes
  MD5 checksum 0a5d5bbf204fef0b1f49ad1d420327cc
  Compiled from "Demo2.java"
public class com.zigao.com.utils.Demo2 extends java.lang.Thread
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #12.#36        // java/lang/Thread."<init>":()V
   #2 = Class              #37            // java/lang/Object
   #3 = Methodref          #2.#36         // java/lang/Object."<init>":()V
   #4 = Fieldref           #11.#38        // com/zigao/com/utils/Demo2.lockObject:Ljava/lang/Object;
   #5 = Methodref          #11.#39        // com/zigao/com/utils/Demo2.metho1:()V
   #6 = Fieldref           #40.#41        // java/lang/System.out:Ljava/io/PrintStream;
   #7 = String             #42            // 我是metho1调用metho2
   #8 = Methodref          #43.#44        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #9 = Methodref          #11.#45        // com/zigao/com/utils/Demo2.metho2:()V
  #10 = String             #46            // 我是etho2
  #11 = Class              #47            // com/zigao/com/utils/Demo2
  #12 = Class              #48            // java/lang/Thread
  #13 = Utf8               lockObject
  #14 = Utf8               Ljava/lang/Object;
  #15 = Utf8               <init>
  #16 = Utf8               ()V
  #17 = Utf8               Code
  #18 = Utf8               LineNumberTable
  #19 = Utf8               LocalVariableTable
  #20 = Utf8               this
  #21 = Utf8               Lcom/zigao/com/utils/Demo2;
  #22 = Utf8               run
  #23 = Utf8               metho1
  #24 = Utf8               StackMapTable
  #25 = Class              #47            // com/zigao/com/utils/Demo2
  #26 = Class              #37            // java/lang/Object
  #27 = Class              #49            // java/lang/Throwable
  #28 = Utf8               metho2
  #29 = Utf8               main
  #30 = Utf8               ([Ljava/lang/String;)V
  #31 = Utf8               args
  #32 = Utf8               [Ljava/lang/String;
  #33 = Utf8               MethodParameters
  #34 = Utf8               SourceFile
  #35 = Utf8               Demo2.java
  #36 = NameAndType        #15:#16        // "<init>":()V
  #37 = Utf8               java/lang/Object
  #38 = NameAndType        #13:#14        // lockObject:Ljava/lang/Object;
  #39 = NameAndType        #23:#16        // metho1:()V
  #40 = Class              #50            // java/lang/System
  #41 = NameAndType        #51:#52        // out:Ljava/io/PrintStream;
  #42 = Utf8               我是metho1调用metho2
  #43 = Class              #53            // java/io/PrintStream
  #44 = NameAndType        #54:#55        // println:(Ljava/lang/String;)V
  #45 = NameAndType        #28:#16        // metho2:()V
  #46 = Utf8               我是etho2
  #47 = Utf8               com/zigao/com/utils/Demo2
  #48 = Utf8               java/lang/Thread
  #49 = Utf8               java/lang/Throwable
  #50 = Utf8               java/lang/System
  #51 = Utf8               out
  #52 = Utf8               Ljava/io/PrintStream;
  #53 = Utf8               java/io/PrintStream
  #54 = Utf8               println
  #55 = Utf8               (Ljava/lang/String;)V
{
  private java.lang.Object lockObject;
    descriptor: Ljava/lang/Object;
    flags: ACC_PRIVATE

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

  public void run();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #5                  // Method metho1:()V
         4: return
      LineNumberTable:
        line 9: 0
        line 10: 4
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/zigao/com/utils/Demo2;

  private void metho1();
    descriptor: ()V
    flags: ACC_PRIVATE
    Code:
      stack=2, locals=3, args_size=1
         0: aload_0
         1: getfield      #4                  // Field lockObject:Ljava/lang/Object;
         4: dup
         5: astore_1
         6: monitorenter
         7: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
        10: ldc           #7                  // String 我是metho1调用metho2
        12: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        15: aload_0
        16: invokespecial #9                  // Method metho2:()V
        19: aload_1
        20: monitorexit
        21: goto          29
        24: astore_2
        25: aload_1
        26: monitorexit
        27: aload_2
        28: athrow
        29: return
      Exception table:
         from    to  target type
             7    21    24   any
            24    27    24   any
      LineNumberTable:
        line 13: 0
        line 14: 7
        line 15: 15
        line 16: 19
        line 17: 29
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      30     0  this   Lcom/zigao/com/utils/Demo2;
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 24
          locals = [ class com/zigao/com/utils/Demo2, class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 4

  private void metho2();
    descriptor: ()V
    flags: ACC_PRIVATE
    Code:
      stack=2, locals=3, args_size=1
         0: aload_0
         1: getfield      #4                  // Field lockObject:Ljava/lang/Object;
         4: dup
         5: astore_1
         6: monitorenter
         7: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
        10: ldc           #10                 // String 我是etho2
        12: invokevirtual #8                  // 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: return
      Exception table:
         from    to  target type
             7    17    20   any
            20    23    20   any
      LineNumberTable:
        line 20: 0
        line 21: 7
        line 22: 15
        line 23: 25
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      26     0  this   Lcom/zigao/com/utils/Demo2;
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 20
          locals = [ class com/zigao/com/utils/Demo2, class java/lang/Object ]
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 4

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=0, locals=1, args_size=1
         0: return
      LineNumberTable:
        line 28: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       1     0  args   [Ljava/lang/String;
    MethodParameters:
      Name                           Flags
      args
}
SourceFile: "Demo2.java"

通过汇编指令我们可以查看到synchronized底层是个monitorenter和monitorexit指令

我们发现一个synchronized有两个退出退出指令monitorexit

 

 当加上synchronized之后代码中会有两次monitorexit。

为什么会有两次了,因为在使用Sychronized同步代码的时候,一个正常的代码结束monitorexit 还有一个异常的monitorexit退出,其中两个里面只能走一个。

synchronized原理实际上是触发monitorenter和monitorexit指令

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值