为什么Java和C#会为每个对象添加内部锁?
使每个对象可锁定看起来像一个设计错误:
- 您为每个创建的对象添加了额外的成本,即使您实际上只在一小部分对象中使用它。
- 锁定用法变得隐含,具有
lockMap.get(key).lock()
比任意对象上的同步更具可读性,例如,synchronize (key) {...}
。 - 同步方法可能会导致用户使用synchronized方法锁定对象时出现细微错误
- 您可以确定在将对象传递给第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
- 更不用说每个对象的命名空间压缩(在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”对象更自由且更有用。
您还在语言或库中添加“锁定”对象吗? 看起来像一个语言结构,但库中的对象很少(如果有的话)有特殊处理,但更多地将线程作为库构造处理可能会减慢速度。