JVM中的Synchronization是使用monitor entry和exit来实现的。不管是显式的还是隐式的。显式的是通过使用monitorenter和monitorexit这个两个指令来实现的。隐式的是通过method的调用以及return的指令来实现的。
在Java编程语言中,同步用到最多的地方恐怕就是synchronized方法了。synchronized方法不是用上面说到的monitorenter和monitorexit来实现的。
而是简单的通过读取在run-time constant pool中的ACC_SYNCHRONIZED flag来识别的。这个ACC_SYNCHRONIZEDflag通过方法调用指令来检查的。其实就是设置了个标识位。
monitorenter和monitorexit这两个指令被用作synchronized statements的编译工作上。也就是我们常说的“同步块”。像下面这样:
void onlyMe(Foo f) {
synchronized(f) {
doSomething();
}
}
上面这段代码被编译成下面这个样子:
Method void onlyMe(Foo)
0 aload_1 // Push f
1 dup // Duplicate it on the stack
2 astore_2 // Store duplicate in local variable 2
3 monitorenter // Enter the monitor associated with f
4 aload_0 // Holding the monitor, pass this and...
5 invokevirtual #5 // ...call Example.doSomething()V
8 aload_2 // Push local variable 2 (f)
9 monitorexit // Exit the monitor associated with f
10 goto 18 // Complete the method normally
13 astore_3 // In case of any throw, end up here
14 aload_2 // Push local variable 2 (f)
15 monitorexit // Be sure to exit the monitor! 16 aload_3 // Push thrown value... 17 athrow // ...and rethrow value to the invoker
18 return // Return in the normal case
Exception table:
From To Target Type
4 10 13 any
13 16 13 any
编译器会保证不管方法调用是怎么完成的,都要保证monitorexit指令最终被执行,只要monitorenter指令被执行了,那么monitorexit必须也要被执行。也就是他们两个必须成对出现做事情。不管是方法调用正常完成还是抛出异常而结束。为了能够强制这对指令在任何情况下都成对出现,编译器生成一些exception handlers,这些handler声称将会匹配可能出现的任何异常,并且每个handler相关的代码会保证执行monitorexit指令。
这极有可能是东半球最具良心的公众号,也是正义的公众号!