用于Java中的实例方法同步的等效代码
在讨论Java同步问题时 ,有人评论说下面的代码片段不相同(并且可能编译为不同的字节码):
public synchronized void someMethod() { //stuff }
和
public void someMethod() { synchronized (this) { //stuff } }
它们是等价的吗?
虽然我测试的编译器(Java 1.6.0_07和Eclipse 3.4)生成不同的字节码,但它们在function上是等效的。 第一个产生:
// access flags 33 public synchronized someMethod()V RETURN
第二个产生:
// access flags 1 public someMethod()V ALOAD 0 DUP MONITORENTER MONITOREXIT RETURN
(感谢ASM进行字节码打印)。
因此它们之间的区别仍然存在于字节码级别,并且由JVM决定它们的行为是否相同。 但是,它们具有相同的function效果 – 请参阅Java语言规范中的示例 。
应该注意的是,如果在子类中重写该方法,则它不一定是同步的 – 因此在这方面也没有区别。
我还运行了一个测试来阻止一个线程试图在每种情况下访问监视器,以比较它们的堆栈跟踪在线程转储中的样子,并且它们都包含有问题的方法,因此也没有区别。
我做了原始评论,声明是相同的。
在这两种情况下,首先发生的事情是调用线程将尝试获取当前对象(意思是this
‘)监视器。
我不知道不同的字节码,我会很高兴听到差异。 但在实践中,它们是完全相同的。
编辑:我将澄清这一点,因为有些人在这里弄错了。 考虑:
public class A { public synchronized void doStuff() { // do stuff } } public class B extends A { public void doStuff() { // do stuff // THIS IS OVERRIDE! } }
在这种情况下,类B中的doStuff()
仍会覆盖类A中的doStuff()
,即使它未同步。
同步关键字永远不是合同的一部分! 不适用于子类,不适用于接口,不适用于抽象类。
我做了原始评论。 我的评论是它们在逻辑上是等价的,但是编译成不同的字节码 。
我当时没有添加任何其他东西来certificate它是正确的,因为真的没有多少理由 – 它们只是编译成不同的字节码。 如果将方法声明为synchronized ,则该同步是方法定义的一部分。 方法中的同步块不是方法定义的一部分 ,而是涉及单独的字节码来获取和释放监视器,如上面的一张海报所示。 严格来说,它们略有不同,但从程序的整体逻辑来看 ,它们是等价的 。
什么时候重要? 好吧,在大多数现代桌面虚拟机上,几乎没有。 但是例如:
- VM原则上可以在一种情况下进行优化,但不能在另一种情况下进行优化
- 有一些JIT编译器优化,其中方法中的字节码数量被作为优化的标准
- 没有 JIT编译器的VM(现在很少见,但可能在较旧的移动设备上?)将在每次调用时处理更多的字节码
是。 在实例方法上使用synchronized关键字使用’this’作为监视器,在类方法(静态方法)上使用它也使用类’Class对象(Foo.class)。
这样,您可以同步整个方法,同时使用synchronized-block样式将其与另一个方法中的代码片段同步。
我看不到任何function差异 – 两者同步它们的整个方法体(this)。 评论这些不同的人如何certificate他们的陈述是正确的?
它们在function上并不完全相同。 其他代码可以使用reflection来查看您的方法是否具有synchronized修饰符,但无法判断方法是否包含同步块而不读取其字节码。
确定方法是否偶尔同步的能力派上用场。 就个人而言,在面向方面编程中进行同步时,我使用该标志来避免冗余锁定。
MyObject myObjectA; MyObject myObjectB; public void someMethod() { synchronized (this) { //stuff } } public void someMethodA() { synchronized (myObjectA) { //stuff } } public void someMethodB() { synchronized (myObjectB) { //stuff } }
在这种情况下:
-
someMethod
阻止整个类 -
someMethodA
仅阻止myObjectA
-
someMethodB
仅阻止myObjectB
-
someMethodA
和someMethodB
可以同时调用