我一直在阅读Java Virtual Machine Instruction Set并注意到当使用指令调用标记为synchronized的方法(例如invokestatic,invokevirtual等)时,由特定的字节码指令来获取监视器在接收器对象上。类似地,从方法返回时,由方法同步时指令释放监视器的指令。这看起来很奇怪,因为有明确的monitorenter和monitorexit字节码来管理监视器。 JVM是否有特殊原因以这种方式设计这些指令,而不是仅仅编译方法以在适当的位置包含monitorenter和monitorexit指令?
答案 0 :(得分:4)
所以你要调用这些同步方法很多。即使Vector
有他们!您可以在没有额外字节码的情况下进行处理。
但不仅仅是代码运行时。类文件更大。额外的说明,但也设置了try
/ finally
表格,并验证了一些顽皮的内容没有被删除。
只是我的猜测。
答案 1 :(得分:3)
您是否在问为什么有两种方法可以做同样的事情?
当一个方法市场同步时,也有多余的monitorenter / exit指令。如果它只有monitorenter / exit指令你就不会打赌从外部看到该方法是同步的(没有读取实际代码)
有两个或更多方法做同样事情的例子。每个人都有相对的优势和劣势。 (例如,许多单字节指令是双字节指令的短版本)
编辑:我必须遗漏问题,因为调用者不需要知道被调用者是否已同步public static void main(String... args) {
print();
printSynchronized();
printSynchronizedInternally();
}
public static void print() {
System.out.println("not synchronized");
}
public static synchronized void printSynchronized() {
System.out.println("synchronized");
}
public static void printSynchronizedInternally() {
synchronized(Class.class) {
System.out.println("synchronized internally");
}
}
生成代码
public static void main(java.lang.String[]);
Code:
0: invokestatic #2; //Method print:()V
3: invokestatic #3; //Method printSynchronized:()V
6: invokestatic #4; //Method printSynchronizedInternally:()V
9: return
public static void print();
Code:
0: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #6; //String not synchronized
5: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
public static synchronized void printSynchronized();
Code:
0: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #8; //String synchronized
5: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
public static void printSynchronizedInternally();
Code:
0: ldc_w #9; //class java/lang/Class
3: dup
4: astore_0
5: monitorenter
6: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream;
9: ldc #10; //String synchronized internally
11: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
14: aload_0
15: monitorexit
16: goto 24
19: astore_1
20: aload_0
21: monitorexit
22: aload_1
23: athrow
24: return
Exception table:
from to target type
6 16 19 any
19 22 19 any
}
答案 2 :(得分:1)
<speculation>
通过将锁管理委派给调用者,现在可以进行一些优化。例如,假设您有一个这样的类:
public class Foo {
public synchronized void bar() {
// ...
}
}
并假设此调用者类使用它:
public class Caller {
public void call() {
Foo foo = new Foo();
// implicit MONITORENTER on foo's lock
foo.bar();
// implicit MONITOREXIT on foo's lock
}
}
基于转义分析,JVM知道foo
永远不会逃脱该线程。因此,它可以避免隐含的MONITORENTER
和MONITOREXIT
指令。
在JVM的早期阶段,当速度是一种罕见的商品时,避免不必要的锁定可能会更多地受性能驱动。
</speculation>
答案 3 :(得分:0)
您是否在问JOD是否可以通过查看方法的属性来推断它们时,为什么同步方法使用显式监视器入口和退出指令?
我猜这是因为,除了方法之外,还可以同步任意代码块:
synchronized( some_object ){ // monitorentry some_object
System.out.println("I am synchronised!");
} // monitorexit some_object
因此,对同步方法和同步块使用相同的指令是有意义的。
答案 4 :(得分:0)
正在搜索同一个问题,并发现了以下文章。看起来方法级同步生成比块级同步稍高效的字节代码。对于块级同步,生成显式字节代码以处理未对方法级同步进行的异常。所以可能的答案是,这两种方法用于使方法级同步稍快一些。
http://www.ibm.com/developerworks/ibm/library/it-haggar_bytecode/