【问题标题】:Static versus non-static lock object in synchronized block同步块中的静态与非静态锁定对象
【发布时间】:2013-08-21 11:58:42
【问题描述】:

试图形象化和理解同步

  1. 使用静态锁对象(代码A)和非静态锁对象有什么区别 同步块的strong>(代码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
  }
}

注意

上面的代码显示了构造函数,但你也可以讨论一下静态方法和非静态方法的行为有何不同。另外,当同步块修改静态成员变量时,使用静态锁会有好处吗?

我已经看过this question中的答案,但还不够清楚不同的使用场景是什么。

【问题讨论】:

  • "请注意,上面的代码显示了构造函数,"这是一个重要的区别。在构造函数上带有实例字段的同步块绝对没有意义,因为没有两个线程会在同一个实例上执行构造函数。
  • 实际上,想想看,实例字段可以包含一个线程间共享的对象。例如private final Object lock = "niceLiteralString";.
  • 我知道使用构造函数来展示一个例子是个坏主意。我的问题也延伸到静态和非静态方法。另外,共享静态/实例字段如何影响锁定?
  • "共享静态/实例字段如何影响锁定?"锁不在字段上,而是在字段包含的 object instance 上。所以synchronized (a){}; synchronized(b){}; synchronized(c){} 最终可能都使用同一个锁。如果该字段为空,它们也可以抛出 NullPointerExceptions。
  • @Thilo this article 提出应该避免使用字符串文字是有充分理由的——JVM 重用引用。

标签: java static synchronization locking synchronized


【解决方案1】:

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

如果字段是非静态的,那么每个实例都会有自己的锁,所以只有在同一对象上调用方法才会相互锁定。

当你使用静态锁对象时:

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

当你使用非静态锁对象时:

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

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

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

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

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

【讨论】:

  • 我认为以构造函数为例是个坏主意。无论如何,很好的答案。我想再问一个问题。有没有办法确保在其他线程访问该成员变量之前,通过一个线程对 volatile static 成员变量进行操作的一组方法将完成并结束?如有必要,我可以将其作为一个单独的问题。
  • @ADTC:对我来说,这听起来像是一个单独的问题。简短的回答:不,你需要同步。只有当 volatile 的操作是 atomic 时才可以避免它(例如,int** 上的单个 ++ 并且**您不关心订单(即您不关心'不在乎你是在手术之前还是之后阅读)。
  • 我确实关心读取,它应该只发生在操作之后。我想我会作为一个单独的问题提出。
  • @ADTC:实际上,我误会了你:即使x++ 在 volatile x 上也不是原子的。请继续为此提出一个新问题。
  • 如果两个线程正在访问同一个对象的两个diff非静态方法,有可能或者第二个线程必须等待,一个对象上的锁是否会锁定所有非静态方法??
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-02
  • 2010-10-01
  • 1970-01-01
  • 2013-08-06
  • 1970-01-01
相关资源
最近更新 更多