字节码分析-同步
源码
package java.bytecode;
/**
* 同步示例 - 演示synchronized方法
*/
public class SynchronizedExample {
private int counter = 0;
// synchronized实例方法
public synchronized void synchronizedMethod() {
counter++;
System.out.println("synchronizedMethod(): counter = " + counter);
}
// synchronized静态方法
public static synchronized void synchronizedStaticMethod() {
System.out.println("synchronizedStaticMethod()");
}
// 同步代码块
public void synchronizedBlock() {
synchronized (this) {
counter++;
System.out.println("synchronizedBlock(): counter = " + counter);
}
}
public int getCounter() {
return counter;
}
}
结构化反解析
Classfile /D:/zcp/github/yggdrasil/java/bytecode/SynchronizedExample.class
...
{
public synchronized void synchronizedMethod();
descriptor: ()V
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
Code:
stack=3, locals=1, args_size=1
0: aload_0
1: dup
2: getfield #7 // Field counter:I
5: iconst_1
6: iadd
7: putfield #7 // Field counter:I
10: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream;
...
38: return
public static synchronized void synchronizedStaticMethod();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
Code:
stack=2, locals=0, args_size=0
0: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #41 // String synchronizedStaticMethod()
5: invokevirtual #35 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
public void synchronizedBlock();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=3, locals=3, args_size=1
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: aload_0
5: dup
6: getfield #7 // Field counter:I
9: iconst_1
10: iadd
11: putfield #7 // Field counter:I
14: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream;
...
42: aload_1
43: monitorexit
44: goto 52
47: astore_2
48: aload_1
49: monitorexit
50: aload_2
51: athrow
52: return
Exception table:
from to target type
4 44 47 any
47 50 47 any
}
详细说明
ACC_SYNCHRONIZED标志
同步方法使用ACC_SYNCHRONIZED标志:
public synchronized void synchronizedMethod();
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
JVM会自动在方法调用时获取锁,方法返回时释放锁。
monitorenter/monitorexit
同步代码块使用monitorenter和monitorexit指令:
public void synchronizedBlock();
Code:
0: aload_0 // 加载this
1: dup
2: astore_1 // 存储到局部变量1
3: monitorenter // 获取锁
4: ... // 同步代码块
42: aload_1
43: monitorexit // 释放锁
44: goto 52
47: astore_2 // 异常处理
48: aload_1
49: monitorexit // 确保异常时也释放锁
50: aload_2
51: athrow
52: return
异常处理
同步代码块需要确保在异常时也能释放锁,因此有多个monitorexit:
Exception table:
from to target type
4 44 47 any
47 50 47 any
同步方法 vs 同步代码块
| 特性 | 同步方法 | 同步代码块 |
|---|---|---|
| 实现方式 | ACC_SYNCHRONIZED标志 | monitorenter/monitorexit |
| 锁对象 | 实例方法:this 静态方法:Class对象 |
显式指定 |
| 性能 | 稍慢(JVM自动处理) | 稍快(更细粒度控制) |
| 灵活性 | 低 | 高 |
锁的获取和释放
- monitorenter:获取对象的监视器锁
- monitorexit:释放对象的监视器锁
- 每个
monitorenter必须对应一个monitorexit
可重入性
Java的synchronized是可重入的:同一个线程可以多次获取同一个锁。
性能考虑
- 同步方法:JVM自动处理,但粒度较粗
- 同步代码块:可以更细粒度控制,减少锁竞争