在讨论Java同步question时,有人评论说下面的代码片段不相同(并且可能编译为不同的字节码):
public synchronized void someMethod() {
//stuff
}
和
public void someMethod() {
synchronized (this) {
//stuff
}
}
他们是等同的吗?
答案 0 :(得分:13)
它们在功能上是等价的,尽管我测试的编译器(Java 1.6.0_07和Eclipse 3.4)生成不同的字节码。第一个产生:
// access flags 33
public synchronized someMethod()V
RETURN
第二个产生:
// access flags 1
public someMethod()V
ALOAD 0
DUP
MONITORENTER
MONITOREXIT
RETURN
(感谢ASM字节码打印)。
因此它们之间的区别仍然存在于字节码级别,并且由JVM决定它们的行为是否相同。但是,它们具有相同的功能效果 - 请参阅Java语言规范中的example。
应该注意的是,如果在子类中重写该方法,则它不一定是同步的 - 因此在这方面也没有区别。
我还运行了一个测试来阻止尝试访问监视器的线程在每种情况下比较它们的堆栈跟踪在线程转储中的样子,并且它们都包含有问题的方法,因此也没有区别。
答案 1 :(得分:7)
我做了原始评论,声明是相同的。
在这两种情况下,首先发生的事情是调用线程将尝试获取当前对象(即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()
,即使它未同步。
同步关键字永远不是合同的一部分!不适用于子类,不适用于接口,不适用于抽象类。
答案 2 :(得分:4)
我做了原始评论。我的评论是它们在逻辑上是等价的,但是编译成不同的字节码。
我当时没有添加任何其他东西来证明它是正确的,因为没有太多理由 - 他们只是做编译到不同的字节码。如果将方法声明为 synchronized ,则该同步是方法定义的一部分。 方法中的同步块不是方法定义的一部分,而是涉及单独的字节码来获取和释放监视器,正如上面的一张海报所示。严格来说,它们略有不同,但与程序的整体逻辑相比,它们相当于。
什么时候重要?好吧,在大多数现代桌面虚拟机上,几乎没有。但是例如:
答案 3 :(得分:2)
是。在实例方法上使用synchronized关键字使用'this'作为监视器,同时在类方法上使用它(静态方法)使用类'Class object(Foo.class)。
通过这种方式,您可以同步整个方法,同时使用synchronized-block样式将其与另一个方法中的代码段同步。
答案 4 :(得分:0)
我看不到任何功能差异 - 两者同步它们的整个方法体(this)。评论这些不同的人如何证明他们的陈述是正确的?
答案 5 :(得分:0)
它们的功能并不完全相同。其他代码可以使用反射来查看您的方法是否具有synchronized修饰符,但是无法判断方法是否包含同步块而不读取其字节码。
确定方法是否偶尔同步的能力派上用场。就个人而言,在面向方面编程中进行同步时,我已经使用该标志来避免冗余锁定。
答案 6 :(得分:-1)
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
可以同时调用