tl;dr - 外部同步使您容易受到攻击(有意或无意),并且还迫使您进行可能不必要的锁定检查。在这个答案的最底部还有一些书呆子的好信息。
同步方法几乎相同(见底部)以同步:
synchroinzed void foo() {
}
void foo() {
synchronized(this) {
}
}
通过使方法本身不是synchronized,您允许自己锁定任何Object,而不仅仅是this。我个人建议在内部Object 上同步,就像这样
private final Object foolock = new Object();
void foo() {
synchronzied(foolock) {
}
}
原因是,如果你使用synchronized 方法,它会有效地锁定this,其他人可以synchronize 锁定你,并将你锁定在Object 之外!想象一下:
class FooDoer {
// removed! using synchronized methods instead
//final Object foolock = new Object();
synchronized void foo() {
}
}
// thread 1 - attacker
FooDoer f = new FooDoer();
globalMap.put("TheFoo",f);
synchronized(f) {
while(true); // haha!
}
// thread 2 - victim
FooDoer f = globalMap.get("TheFoo");
f.foo(); // locked, because Thread 1 has locked us out!
这会让您面临拒绝服务攻击。这不好!通过将锁设置为内部,您作为课程的作者可以准确控制谁可以锁定您对象的哪些区域以及在什么条件下。
另一个问题是您可能没有要检查的受保护数据。例如:
synchronized void foo() {
if(expensiveAccessCheck()) {
update();
}
}
void foo() {
if(expensiveAccessCheck()) {
synchronized(foolock) {
update();
}
}
}
在这种情况下,您不必让其他人在您坐在那里转动车轮时等待。也许您正在从 URL 中抓取数据,然后进行更新。为什么要让其他人远离数据?在这种情况下,粒度越低越好。
现在您可能还记得我之前说的几乎相同。两者之间有一个微小的,微小的,微小的差异。同步方法会将同步指令直接烘焙到字节码中的方法签名中。这将使字节码缩短 1 个字节,因为它不需要进行额外的调用。这可能会产生很小的影响,因为方法的字节码中的字节数是确定是否内联的因素之一。尽管有这些细节,我强烈建议将它们视为相同,因为这是一种微优化,在生产环境中几乎不会发挥重要作用。当它们不是相同时,将其称为相同是不完整的。