避免在 Java 中使用 synchronized(this)?

新手上路,请多包涵

每当在 SO 上弹出有关 Java 同步的问题时,有些人会非常急切地指出 synchronized(this) 应该避免。相反,他们声称,首选锁定私有引用。

一些给出的原因是:

包括我在内的其他人认为 synchronized(this) 是一个经常使用(也在 Java 库中)的惯用语,是安全且易于理解的。它不应该被避免,因为你有一个错误并且你不知道你的多线程程序中发生了什么。换句话说:如果它适用,那就使用它。

我有兴趣看到一些真实世界的例子(没有 foobar 的东西),其中避免锁定 this 是更可取的,而 synchronized(this) 也可以完成这项工作。

因此: 您是否应该始终避免 synchronized(this) 并将其替换为私有引用上的锁?


一些进一步的信息(根据给出的答案进行更新):

  • 我们正在谈论实例同步
  • 隐式( synchronized 方法)和 synchronized(this) 的显式形式都被考虑
  • 如果您引用 Bloch 或其他有关该主题的权威,请不要遗漏您不喜欢的部分(例如 Effective Java,Thread Safety 上 的项目:通常是实例本身的锁,但也有例外。)
  • 如果您需要锁定粒度而不是 synchronized(this) 提供,那么 synchronized(this) 不适用,所以这不是问题

原文由 eljenso 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 489
2 个回答

我将分别介绍每一点。

  1. > 一些邪恶的代码可能会窃取你的锁(这个很流行,也有一个“意外”的变体)

我更担心 _一不小心_。它相当于 this 的这种使用是您类的公开接口的一部分,应该记录在案。有时需要其他代码使用您的锁的能力。这对于像 Collections.synchronizedMap 这样的事情是正确的(参见 javadoc)。

  1. > 同一类中的所有同步方法使用完全相同的锁,这会降低吞吐量

这是过于简单化的想法;只是摆脱 synchronized(this) 不会解决问题。吞吐量的正确同步将需要更多考虑。

  1. > 您(不必要地)暴露了太多信息

这是#1 的变体。使用 synchronized(this) 是您界面的一部分。如果您不想/不需要暴露此内容,请不要这样做。

原文由 Darron 发布,翻译遵循 CC BY-SA 3.0 许可协议

那么,首先需要指出的是:

 public void blah() {
  synchronized (this) {
    // do stuff
  }
}

在语义上等同于:

 public synchronized void blah() {
  // do stuff
}

这是不使用 synchronized(this) 的原因之一。您可能会争辩说您可以围绕 synchronized(this) 块做一些事情。通常的原因是尽量避免进行同步检查,这会导致各种并发问题,特别是 双重检查锁定问题,这恰恰表明进行相对简单的检查有多么困难线程安全。

私有锁是一种防御机制,这绝不是一个坏主意。

另外,正如您提到的,私有锁可以控制粒度。对象上的一组操作可能与另一组操作完全无关,但 synchronized(this) 将相互排除对所有这些操作的访问。

synchronized(this) 真的没有给你任何东西。

原文由 cletus 发布,翻译遵循 CC BY-SA 2.5 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题