我有一个带有synchronized方法的类:
class A {
synchronized void method1() {};
synchronized void method2() {};
}
如果某个客户端类想要调用method1()
,那么method2()
知道这些调用是原子的
第一种情况:
class B {
A a;
public void foo() {
synchronized(a) {
a.method1();
a.method2();
}
}
}
第二种情况:
class B {
A a;
final Object lock = new Object();
public void foo() {
synchronized(lock) {
a.method1();
a.method2();
}
}
}
我有什么理由可以使用一个案例而不是另一个案例吗?
答案 0 :(得分:1)
在a上进行同步可能是最有意义的。这至少保持了“最少惊喜”的原则。 a是其他客户端同步的最自然对象。添加一些文档以使其更清晰。另外,您可以保证在A中的同步方法上获得锁定,如果您在A和B中使用不同的锁定,这可能比潜在的竞争锁定更有效。
答案 1 :(得分:0)
如果总是使用第一种技术调用方法,那么这两种方法实际上是原子的:一次只有一个线程(运行method1并运行method2),作为一个块。
在第二种情况下,B
的任何给定实例将以原子方式调用这两个方法 - 但如果两个线程各自具有单独的B
实例,那么method1()
和{ {1}}来电可以交错。想象:
method2()
B b1 = new B(a)
(对于同一个实例B b2 = new B(a)
)a
,获取对b1.newB()
b1.lock
,获取b2.newB()
上的锁定(由于它是b2.lock
之外的单独对象,因此无法争用)b1.lock
a.method1()
但由于已同步a.method1()
a.method1()
a.method1()
,因为Thread2仍在运行a.method2()
您可以通过使method1()
成为静态最终版来解决这个问题,但这可能比您需要的锁定更多 - 现在对B.lock
的所有调用都是序列化的,而不是按{A.method1(); A.method2();}
的实例序列化。
即使你使用第一次同步方法,你仍然受其他人玩得很好的摆布。如果其他人直接调用A
(而不是通过a.method1()
),您仍然可以进行非原子交错。
B.foo
B b1 = new B(a)
(a
的相同实例)a
,获取对b1.newB()
b1.lock
a.method1()
但由于已同步a.method1()
a.method1()
a.method1()
,因为Thread2仍在运行a.method2()
除了限制method1()
和method1()
的可见性之外,您无能为力。例如,您可以将它们打包为私有或受保护,并假设/希望有权访问这些方法的类比滥用它们的权力更好。如果你走那条路,那么你甚至不需要同步method2()
或method1()
- 你可以记录并假设(如果你想的话,甚至是assert)他们会<{1}}被锁定时调用。