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

时间:2012-08-29 19:03:49

标签: c# java synchronization locking

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

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

    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,而不是在wait中重载Object } ...)
    2. 但是我确信这个设计有一些原因。内在锁的好处是什么?

4 个答案:

答案 0 :(得分:1)

  

您为每个创建的对象添加了额外的费用,即使您已经开始   实际上只在一小部分物体中使用它。

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

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

  

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

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

  

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

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

  

您可以确定在将对象传递给第3个分离API时,它是可以的   没有使用锁。

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

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

  

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

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

答案 1 :(得分:0)

实际上你只在每个对象中引用了那个监视器;只有在使用synchronization =>时才会创建真正的监视器对象。没有那么多的记忆丢失了。

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

答案 2 :(得分:0)

一个好处是退出synchronized块时自动解锁,即使是异常。

答案 3 :(得分:0)

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

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

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