Java的每个对象都可以作为锁
表现为3种形式:
- 对于普通方法,锁是当前实例。
- 对于静态方法,锁是当前类的Class对象。
- 对于同步方法,锁是synchronized括号里配置的对象。
JVM基于进入和退出Monitor对象来实现方法同步和代码块同步,但两者的实现细节不一样。
代码块同步是使用monitorenter和monitorexit指令实现的,而方法同步是使用另外一种实现,细节在JVM规范里没有详细说明。
我们来看一下方法同步和代码块同步的字节码形式:
代码块同步
源码:
public class TestSynchronized {
private final static Object MUTEX = new Object();
public static void main(String[] args) {
synchronized (MUTEX) {
System.out.println("1");
}
}
}
对应字节码:
// class version 52.0 (52)
// access flags 0x21
public class bfxx/TestSynchronized {
// compiled from: TestSynchronized.java
// access flags 0x1A
private final static Ljava/lang/Object; MUTEX
// access flags 0x1
public <init>()V
L0
LINENUMBER 7 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
RETURN
L1
LOCALVARIABLE this Lbfxx/TestSynchronized; L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
// access flags 0x9
public static main([Ljava/lang/String;)V
TRYCATCHBLOCK L0 L1 L2 null
TRYCATCHBLOCK L2 L3 L2 null
L4
LINENUMBER 11 L4
GETSTATIC bfxx/TestSynchronized.MUTEX : Ljava/lang/Object;
DUP
ASTORE 1
MONITORENTER
L0
LINENUMBER 12 L0
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "1"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L5
LINENUMBER 13 L5
ALOAD 1
MONITOREXIT
L1
GOTO L6
L2
FRAME FULL [[Ljava/lang/String; java/lang/Object] [java/lang/Throwable]
ASTORE 2
ALOAD 1
MONITOREXIT
L3
ALOAD 2
ATHROW
L6
LINENUMBER 14 L6
FRAME CHOP 1
RETURN
L7
LOCALVARIABLE args [Ljava/lang/String; L4 L7 0
MAXSTACK = 2
MAXLOCALS = 3
// access flags 0x8
static <clinit>()V
L0
LINENUMBER 8 L0
NEW java/lang/Object
DUP
INVOKESPECIAL java/lang/Object.<init> ()V
PUTSTATIC bfxx/TestSynchronized.MUTEX : Ljava/lang/Object;
RETURN
MAXSTACK = 2
MAXLOCALS = 0
}
可以看到在LINENUMBER 12
前面有MONITORENTER
指令,在后面有MONITOREXIT
指令。
方法同步
源码:
public class TestSynchronized {
public static synchronized void test() {
System.out.println("1");
}
}
字节码:
// class version 52.0 (52)
// access flags 0x21
public class bfxx/TestSynchronized {
// compiled from: TestSynchronized.java
// access flags 0x1
public <init>()V
L0
LINENUMBER 7 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
RETURN
L1
LOCALVARIABLE this Lbfxx/TestSynchronized; L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
// access flags 0x29
public static synchronized test()V
L0
LINENUMBER 9 L0
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "1"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
L1
LINENUMBER 10 L1
RETURN
MAXSTACK = 2
MAXLOCALS = 0
}
从字节码没有看到如何实现的进入和退出Monitor对象。
待补充:
- monitor对象是什么?在哪里?
- Java对象头
- 锁升级