0

我有一个带有同步方法的类:

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();
            }    
        }

    }

有什么理由我应该使用一个案例而不是另一个案例?

4

2 回答 2

1

在 a 上同步可能最有意义。这至少保持了“最少意外”的原则。a 也是其他客户端同步的最自然的对象。添加一些文档以使其更清晰。另外,您可以保证在 A 中的同步方法上获得锁,如果您在 A 和 B 中使用不同的锁,这可能比潜在的竞争锁更有效。

于 2013-05-24T19:56:24.310 回答
0

如果您总是使用第一种技术调用方法,那么这两个方法实际上是原子的:一次只有一个线程将(运行方法 1 和运行方法 2)作为一个块。

在第二种情况下,任何给定的实例都B将自动调用这两个方法——但如果两个线程各有一个单独的实例B,则method1()andmethod2()调用可以交错。想象:

  • Thread1 拥有B b1 = new B(a)
  • Thread2 拥有B b2 = new B(a)(对于同一个实例a
  • Thread1 调用b1.newB(),获取一个锁b1.lock
  • Thread2 调用b2.newB(),获取一个锁b2.lock(没有争用,因为它是一个单独的对象而不是b1.lock
  • 线程 1 开始a.method1()
  • Thread2 尝试启动a.method1()但因已同步而被阻止
  • 线程1完成a.method1()
  • 线程 2 开始a.method1()
  • a.method2()Thread1 在 Thread2 仍在运行时启动method1()
    • 没有实现原子性!

您可以通过制作B.lock一个静态 final 来解决这个问题,但这可能比您需要的锁定更多——现在所有对 的调用{A.method1(); A.method2();}都是序列化的,而不是每个A.

即使你使用第一次同步的方法,你仍然受到其他人的摆布。如果其他人直接调用a.method1()(而不是通过B.foo),您仍然可以获得非原子交错。

  • Thread1 拥有B b1 = new B(a)
  • Thread2a直接拥有(的相同实例a
  • Thread1 调用b1.newB(),获取一个锁b1.lock
  • 线程 1 开始a.method1()
  • Thread2 尝试启动a.method1()但因已同步而被阻止
  • 线程1完成a.method1()
  • 线程 2 开始a.method1()
  • a.method2()Thread1 在 Thread2 仍在运行时启动method1()
    • 没有实现原子性!

method1()除了限制and的可见性之外,您对此无能为力method2()。例如,您可以将它们设为包私有或受保护,并假设/希望有权访问这些方法的类比滥用它们的权力更清楚。如果你走那条路,那么你甚至不需要同步method1(),或者method2()——你可以记录并假设(如果你愿意,甚至可以断言)它们将在this锁定时被调用。

于 2013-05-24T19:57:34.760 回答