17

使每个对象都可锁定看起来像是一个设计错误:

  1. 您为创建的每个对象增加了额外的成本,即使您实际上只会在一小部分对象中使用它。
  2. 锁的使用变得隐式,拥有lockMap.get(key).lock()比任意对象上的同步更具可读性,例如synchronize (key) {...}.
  3. 同步方法可能会导致用户使用同步方法锁定对象的细微错误
  4. 您可以确定,在将对象传递给 3rd parting API 时,它的锁没有被使用。

例如

class Syncer {
    synchronized void foo(){}
}
...
Syncer s = new Syncer();
synchronize(s) {
    ...
}
// in another thread
s.foo() // oops, waiting for previous section, deadlocks potential
  1. 更不用说每个对象的命名空间污染(在 C# 中至少方法是静态的,在 Java 中同步原语必须使用,而不是在...await中重载)waitObject

但是我确信这种设计是有原因的。内在锁的最大好处是什么?

4

4 回答 4

1

您为创建的每个对象增加了额外的成本,即使您实际上只会在一小部分对象中使用它。

这是由 JVM 实现决定的。JVM 规范说,“监视器与对象的关联可以通过各种超出本规范范围的方式进行管理。例如,监视器可以与对象同时分配和释放。或者,它可以在线程试图获得对对象的独占访问权时动态分配,并在稍后没有线程保留在对象的监视器中时释放。”

我还没有看过太多 JVM 源代码,但是如果任何常见的 JVM 处理这个效率低下,我会感到非常惊讶。

锁的使用变得隐含,lockMap.get(key).lock() 比任意对象上的同步更具可读性,例如 synchronize (key) {...}。

我完全不同意。一旦你知道 的含义synchronize,它就比一连串的方法调用更具可读性。

同步方法可能会导致用户使用同步方法锁定对象的细微错误

这就是为什么你需要知道 的含义synchronize。如果你阅读了它的作用,那么避免这些错误就变得相当微不足道了。经验法则:不要在多个地方使用同一个锁,除非这些地方需要共享同一个锁。任何语言的锁定/互斥策略都可以这样说。

您可以确定,在将对象传递给 3rd parting API 时,它的锁没有被使用。

正确的。这通常是一件好事。如果它被锁定,应该有一个很好的理由来锁定它。其他线程(第三方或非第三方)需要等待轮到它们。

如果您myObject为了允许其他线程同时使用而进行同步myObject,那么您做错了。如果有帮助,您可以轻松地同步相同的代码块myOtherObject

更不用说每个对象的命名空间污染(在 C# 中至少方法是静态的,在 Java 同步原语中必须使用等待,而不是在 Object 中重载等待......)

该类Object确实包含一些与同步相关的便利方法,即notify()notifyAll()wait()。您不需要使用它们的事实并不意味着它们没有用。你可以很容易地抱怨clone(), equals(),toString()等等。

于 2018-05-15T22:28:06.323 回答
0

实际上,您只能在每个对象中引用该监视器;只有当您使用同步时才会创建真正的监视器对象 => 不会丢失太多内存。

另一种方法是将手动监视器添加到您需要的那些类中;这会使代码非常复杂,并且更容易出错。Java 以性能换取生产力。

于 2012-08-29T19:08:24.120 回答
0

一个好处是在退出synchronized块时自动解锁,即使是例外。

于 2012-08-29T19:08:33.150 回答
0

我假设像 toString() 一样,设计者认为好处大于成本。

必须做出很多决定,并且很多概念都未经测试(检查异常确认!),但总的来说,我确信它比显式的“锁定”对象更自由且更有用。

您还向语言或库添加“锁定”对象吗?似乎是一种语言结构,但库中的对象很少(如果有的话?)有特殊处理,但将线程更多地视为库结构可能会减慢速度。

于 2012-08-29T19:11:22.637 回答