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指令