synchronized 关键字锁浅析

1. 编写java代码

package com.seeyon.test;

/**
* @Author:
* @CreateTime: 2022-1-9 21:59
* @Description:
*/
public class SynTest {

public void synBlock() {
synchronized (this) {
System.out.println("synchronized 详解");
}
}
}

###2. 编译 反汇编

D:\open_source\DXZW\src\com\seeyon\test>javac SynTest.java

D:\open_source\DXZW\src\com\seeyon\test>javap -verbose SynTest.class
Classfile /D:/open_source/DXZW/src/com/seeyon/test/SynTest.class
Last modified 2022-1-9; size 536 bytes
MD5 checksum 90124e59d243d5c40b89635d570db246
Compiled from "SynTest.java"
public class com.seeyon.test.SynTest
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#18 // java/lang/Object."<init>":()V
#2 = Fieldref #19.#20 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #21 // synchronized 璇﹁В
#4 = Methodref #22.#23 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #24 // com/seeyon/test/SynTest
#6 = Class #25 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 synBlock
#12 = Utf8 StackMapTable
#13 = Class #24 // com/seeyon/test/SynTest
#14 = Class #25 // java/lang/Object
#15 = Class #26 // java/lang/Throwable
#16 = Utf8 SourceFile
#17 = Utf8 SynTest.java
#18 = NameAndType #7:#8 // "<init>":()V
#19 = Class #27 // java/lang/System
#20 = NameAndType #28:#29 // out:Ljava/io/PrintStream;
#21 = Utf8 synchronized 璇﹁В
#22 = Class #30 // java/io/PrintStream
#23 = NameAndType #31:#32 // println:(Ljava/lang/String;)V
#24 = Utf8 com/seeyon/test/SynTest
#25 = Utf8 java/lang/Object
#26 = Utf8 java/lang/Throwable
#27 = Utf8 java/lang/System
#28 = Utf8 out
#29 = Utf8 Ljava/io/PrintStream;
#30 = Utf8 java/io/PrintStream
#31 = Utf8 println
#32 = Utf8 (Ljava/lang/String;)V
{
public com.seeyon.test.SynTest();
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 8: 0

public void synBlock();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=1
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #3 // String synchronized 璇﹁В
9: invokevirtual #4 // 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
LineNumberTable:
line 11: 0
line 12: 4
line 13: 12
line 14: 22
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 17
locals = [ class com/seeyon/test/SynTest, class java/lang/Object ]
stack = [ class java/lang/Throwable ]
frame_type = 250 /* chop */
offset_delta = 4
}
SourceFile: "SynTest.java"

synchronized 代码块实际上多个monitorenter 和monitorexit指令。这里的monitorenter有两个monitorexit指令的原因是,jvm要保证每个 monitorenter必须有与之对应的monitorexit, monitorenter 指令被插入到同步代码块开始位置,而monitorexit需要插入到方法正常结束出和异常处两个地方,这样就可以保证抛异常的情况下也能释放锁。