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

2022-01-22 00:00:00 synchronization locking c# java

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

Making every object lockable looks like a design mistake:

  1. 您为创建的每个对象都增加了额外的成本,即使您实际上只会在一小部分对象中使用它.
  2. 锁的使用变得隐式,使用 lockMap.get(key).lock() 比在任意对象上同步更具可读性,例如 synchronize (key) {...}.
  3. 同步方法可能会导致用户使用同步方法锁定对象的细微错误
  4. 您可以确定,在将对象传递给 3rd parting API 时,它的锁没有被使用.
  1. You add extra cost for every object created, even though you'll actually use it only in a tiny fraction of the objects.
  2. Lock usage become implicit, having lockMap.get(key).lock() is more readable than synchronization on arbitrary objects, eg, synchronize (key) {...}.
  3. Synchronized methods can cause subtle error of users locking the object with the synchronized methods
  4. You can be sure that when passing an object to a 3rd parting API, it's lock is not being used.

例如

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对象...)
  1. Not to mention the namespace polution for each and every object (in C# at least the methods are static, in Java synchronization primitives have to use await, not to overload wait in Object...)

但是我确信这种设计是有原因的.内在锁的最大好处是什么?

However I'm sure there is some reason for this design. What is the great benefit of intrinsic locks?

推荐答案

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

You add extra cost for every object created, even though you'll actually use it only in a tiny fraction of the objects.

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

That's determined by the JVM implementation. The JVM specification says, "The association of a monitor with an object may be managed in various ways that are beyond the scope of this specification. For instance, the monitor may be allocated and deallocated at the same time as the object. Alternatively, it may be dynamically allocated at the time when a thread attempts to gain exclusive access to the object and freed at some later time when no thread remains in the monitor for the object."

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

I haven't looked at much JVM source code yet, but I'd be really surprised if any of the common JVMs handled this inefficiently.

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

Lock usage become implicit, having lockMap.get(key).lock() is more readable than synchronization on arbitrary objects, eg, synchronize (key) {...}.

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

I completely disagree. Once you know the meaning of synchronize, it's much more readable than a chain of method calls.

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

Synchronized methods can cause subtle error of users locking the object with the synchronized methods

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

That's why you need to know the meaning of synchronize. If you read about what it does, then avoiding these errors becomes fairly trivial. Rule of thumb: Don't use the same lock in multiple places unless those places need to share the same lock. The same thing could be said of any language's lock/mutex strategy.

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

You can be sure that when passing an object to a 3rd parting API, it's lock is not being used.

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

Right. That's usually a good thing. If it's locked, there should be a good reason why it's locked. Other threads (third party or not) need to wait their turns.

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

If you synchronize on myObject with the intent of allowing other threads to use myObject at the same time, you're doing it wrong. You could just as easily synchronize the same code block using myOtherObject if that would help.

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

Not to mention the namespace polution for each and every object (in C# at least the methods are static, in Java synchronization primitives have to use await, not to overload wait in Object...)

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

The Object class does include some convenience methods related to synchronization, namely notify(), notifyAll(), and wait(). The fact that you haven't needed to use them doesn't mean they aren't useful. You could just as easily complain about clone(), equals(), toString(), etc.

相关文章