为什么Java和C#会为每个对象添加内部锁?

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

  1. 您为每个创建的对象添加了额外的成本,即使您实际上只在一小部分对象中使用它。
  2. 锁定用法变得隐含,具有lockMap.get(key).lock()比任意对象上的同步更具可读性,例如, synchronize (key) {...}
  3. 同步方法可能会导致用户使用synchronized方法锁定对象时出现细微错误
  4. 您可以确定在将对象传递给第3个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 ,而不是在Object重载wait …)

但是我确信这个设计有一些原因。 内在锁的好处是什么?

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

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

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

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

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

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

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

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

对。 这通常是一件好事。 如果它被锁定,应该有一个很好的理由为什么它被锁定。 其他线程(第三方与否)需要等待轮到他们。

如果你在myObject上同步以允许其他线程同时使用myObject ,那么你做错了。 您可以使用myOtherObject轻松同步相同的代码块,如果这有帮助的话。

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

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

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

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

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

我认为像toString()一样,设计师认为好处超过了成本。

必须做出很多决定并且许多概念未经测试(Checked exceptions-ack!)但总的来说我确信它比显式的“Lock”对象更自由且更有用。

您还在语言或库中添加“锁定”对象吗? 看起来像一个语言结构,但库中的对象很少(如果有的话)有特殊处理,但更多地将线程作为库构造处理可能会减慢速度。