同步块中的静态与非静态锁定对象

尝试可视化和理解同步

  1. 同步块使用静态锁定对象(代码 A)和非静态锁定对象(代码 B)之间有什么区别?
  2. 它在实际应用中有何不同?
  3. 一个人会有什么陷阱,而另一个人不会有?
  4. 确定使用哪一个的标准是什么?

代码 A

public class MyClass1 {
  private static final Object lock = new Object();
  public MyClass1() {
    //unsync
    synchronized(lock) {
      //sync
    }
    //unsync
  }
}

代码 B

public class MyClass2 {
  private final Object lock = new Object();
  public MyClass2() {
    //unsync
    synchronized(lock) {
      //sync
    }
    //unsync
  }
}

注意

上面的代码显示了构造函数,但您可以讨论静态方法和非静态方法中的行为有何不同。此外,当同步块正在修改静态成员变量时,使用静态锁是否有利?

我已经看过这个问题的答案,但还不够清楚不同的使用场景是什么。


答案 1

区别很简单:如果锁定的对象位于字段中,则 的所有实例都将共享该锁定(即,没有两个对象能够同时锁定该对象)。staticMyClass*

如果该字段是非静态的,则每个实例都有自己的锁,因此只有对同一对象的方法的调用才会相互锁定。

使用静态锁定对象时:

  • 线程 1 调用o1.foo()
  • 线程 2 调用,必须等待线程 1 完成o1.foo()
  • 线程 3 调用 ,还必须等待线程 1(可能还有 2)完成o2.foo()

使用非静态锁定对象时:

  • 线程 1 调用o1.foo()
  • 线程 2 调用,必须等待线程 1 完成o1.foo()
  • 线程 3 调用,它可以继续,而不必担心线程 1 和 2o2.foo()

您需要哪一个取决于您尝试使用同步块保护哪种数据。

根据经验,您希望锁定对象具有与操作打开的值相同的 -ness。因此,如果操作非静态值,则需要一个非静态锁定对象。如果操作静态值,则需要静态锁定对象。static

当您操作静态和非静态值时,它将变得复杂。简单的方法是只使用静态锁对象,但这可能会使同步块的大小超过绝对必要,并且可能需要比预期更多的锁争用。在这些情况下,您可能需要静态和非静态锁定对象的组合。

在您的特定情况下,您在构造函数中使用锁,该构造函数每个实例只能执行一次,因此非静态锁对象在这里没有任何意义。


答案 2

推荐