對如下這樣一個簡單的代碼用javap查看的bytecode信息
public class Demo {
public static void main(String[] args) {
Object lock = new Object();
synchronized (lock) {
System.out.println("ok");
}
}
}
javac -g Demo.java
javap -v Demo
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
-------- 0 ~ 7 爲 Object lock = new Object()對 應的bytecode -----
//new在堆中產生新的對象的同時,將對象的一個引用放入操作數棧
0: new #2 // new Object
//dup複製一份對象的引用,是爲了用2次
3: dup
//執行invokespecial指令,消耗棧頂的一個對象的引用,調用構造方法
4: invokespecial #1
7: astore_1 //第二個lock引用賦給局部變量表中1號slot的name爲lock的局部變量
8: aload_1 //將對象引用加載到操作數棧
9: dup //再複製一份,分別給monitorenter,monitorexit使用,分別爲加鎖和解鎖
10: astore_2 //將剛纔新產生的對象引用 存儲到2號slot(沒有名字的slot)
11: monitorenter //還剩的一個對象引用被monitorenter消耗掉,這就是加鎖操作
-------- 12 ~ 21 爲sychronized代碼塊內部-----
12: getstatic #3 // < - System.out
15: ldc #4 // < = "ok"
17: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
20: aload_2 //將剛纔存到slot_2的對象引用加載到操作數棧給解鎖指令用
21: monitorexit
22: goto 30
//怎樣保證一定會解鎖?利用異常表
25: astore_3 // 將異常對象的引用存到slot_3
26: aload_2 // < - slot_2(lock引用)
27: monitorexit // monitorexit(lock引用),確保對同一個對象解鎖
28: aload_3 //剛纔的異常對象引用加載進來,進行拋出
29: athrow
30: return
Exception table:
from to target type
12 22 25 any
25 28 25 any
LocalVariableTable:
Start Length Slot Name Signature
0 31 0 args [Ljava/lang/String;
8 23 1 lock Ljava/lang/Object;
總結:
1.sychronized
用於對象上時,會產生2份對象的引用,分別給加鎖和解鎖指令用
2.sychronized
用於對象上時,利用異常表,不管sychronized
中的代碼是否正常執行,都將會對同一個對象解鎖