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

2022-09-03 03:06:19

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

  1. 您为创建的每个对象增加了额外的成本,即使您实际上只在一小部分对象中使用它。
  2. 锁用法变得隐式,在任意对象上具有比同步更具可读性,例如,.lockMap.get(key).lock()synchronize (key) {...}
  3. 同步方法可能会导致用户使用同步方法锁定对象的细微错误
  4. 您可以确定,在将对象传递到第三方分型 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. 更不用说每个对象的命名空间polposition(在C#中,至少方法都是静态的,在Java中同步原语必须使用,而不是在...中重载)awaitwaitObject

但是,我相信这种设计是有原因的。固有锁的最大好处是什么?


答案 1

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

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

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

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

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

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

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

您可以确定,在将对象传递到第三方分型 API 时,未使用该对象的锁。

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

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

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

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


答案 2

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

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


推荐