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

为什么Java和C#为每个对象添加了内在锁?,c#,java,synchronization,locking,C#,Java,Synchronization,Locking,使每个对象都可锁定看起来像是一个设计错误: 您为创建的每个对象都增加了额外的成本,即使您实际上只在一小部分对象中使用它 锁的用法变得隐式,使得lockMap.get(key).Lock()比在任意对象上的同步更具可读性,例如,synchronize(key){…} 同步方法可能会导致用户使用同步方法锁定对象的细微错误 您可以确定,当将对象传递给第三个API时,它的锁没有被使用 乙二醇 更不用说每个对象的名称空间污染(在C中,至少方法是静态的,在Java同步原语中必须使用wait,而不是在对象中重

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

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

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

  • 然而,我相信这种设计是有原因的。内在锁的最大好处是什么?

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


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

    一个好处是退出
    同步的
    块时自动解锁,即使是例外。

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

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

    另外,您是否向语言或库添加“锁定”对象?看起来像是一种语言构造,但库中的对象很少(如果有的话?)有特殊处理,但将线程更多地视为库构造可能会减慢速度

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

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

    我还没有看过很多JVM源代码,但是如果有任何一个普通JVM处理这个问题效率低下,我会非常惊讶

    锁的用法变得隐式,使用lockMap.get(key).Lock()更简单 在任意对象上比同步更可读,例如同步 (键){…}

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

    同步方法可能会导致用户锁定 对象的同步方法

    这就是为什么您需要了解同步的含义。如果您了解它的功能,那么避免这些错误就变得相当简单了。经验法则:不要在多个位置使用同一个锁,除非这些位置需要共享同一个锁。任何语言的锁/互斥策略都可以这样说

    您可以确定,当将对象传递给第三个API时 锁未被使用

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

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

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


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

    您在#3中的代码实际上很好:
    synchronized(o){synchronized(o){…}}}
    在Java中非常安全,相当于
    synchronized(o){…}
    。线程不会“锁定自己”,或诸如此类的东西,如果这是你所期望的。实际上,我并不期望“为每一个创建的对象额外花费”太多;似乎表明,当您不实际使用同步时,唯一的成本是~2位。@ruakh我知道锁是可重入的,但问题是人们会在不知不觉中使用您使用的同一个锁。做一些事情,比如在一个线程中锁定映射对象,然后在另一个线程中放入一些东西(已同步)。@LouisWasserman成本不仅以字节为单位,成本还包括API重量、代码大小(在没有同步支持的情况下,为VM提供更大的问题)等等。总之,我看不到任何好处。亲爱的收信人:,我在寻找拥有内在锁的好处和设计考虑。如何改进答案以重新打开它?是的,但这可以通过同步块接受
    Locker
    接口来实现。@ElazarLeibovich只能猜测,但这会使编译器更复杂。而且它可能会使内部JVM特性(如线程转储)更加复杂
    class Syncer {
        synchronized void foo(){}
    }
    ...
    Syncer s = new Syncer();
    synchronize(s) {
        ...
    }
    // in another thread
    s.foo() // oops, waiting for previous section, deadlocks potential