写作时间:2018.07.08
最后更新时间:2018.07.08
public class Sync {
private static Object lock = new Object();
public static void main(String[] args) {
int i = 0;
while (i++ < 100000) {
doSomething();
}
}
public static void doSomething() {
int a = 0;
synchronized (lock) {
a++;
}
}
}
使用参数-XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly编译运行之后,可以看到如下信息:
0x00007f6cbcce3385: jne 0x7f6cbcce33e4 ;*monitorenter
; - Sync::doSomething@7 (line 14)
0x00007f6cbcce338b: lea 0x20(%rsp),%rax
0x00007f6cbcce3390: mov 0x8(%rax),%rdi
0x00007f6cbcce3394: mov (%rdi),%rsi
0x00007f6cbcce3397: and $0x7,%rsi
0x00007f6cbcce339b: cmp $0x5,%rsi
0x00007f6cbcce339f: je 0x7f6cbcce33bc
0x00007f6cbcce33a5: mov (%rax),%rsi
0x00007f6cbcce33a8: test %rsi,%rsi
0x00007f6cbcce33ab: je 0x7f6cbcce33bc
0x00007f6cbcce33b1: lock cmpxchg %rsi,(%rdi)
0x00007f6cbcce33b6: jne 0x7f6cbcce33f4 ;*monitorexit
; - Sync::doSomething@12 (line 16)
可见,在Java中,synchronzied关键字是使用字节码monitorenter和moniterexit来实现的。monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处, JVM要保证每个monitorenter必须有对应的monitorexit与之配对。任何对象都有一个 monitor 与之关联,当且一个monitor 被持有后,它将处于锁定状态。线程执行到 monitorenter 指令时,将会尝试获取对象所对应的 monitor 的所有权,即尝试获得对象的锁。
那么这个所谓的锁在哪呢?
在Hotspot中,对象在内存中存储的布局可以分为3块:对象头,实例数据和为了对齐而做的填充。
对象的锁就保存在对象头的第一个字Mark Word中,在一个64位的虚拟机中,Mark Word就是64位长的,其中有3位和锁有关系,1位表示是否是偏向锁,2位表示锁状态,共有无锁定,轻量级锁定,重量级锁定3种状态。monitorenter和monitorexit就是通过操纵这3位来实现的。
网友评论