JVM是否为每个对象创建一个互斥锁以实现’synchronized’关键字? 如果没有,怎么样?

随着C ++程序员越来越熟悉Java,我看到语言级别支持锁定任意对象而没有任何声明对象支持这种锁定,这有点奇怪。 为每个对象创建互斥锁似乎是一个自动选择的高成本。 除了内存使用,互斥体在某些平台上是OS有限的资源。 如果互斥锁不可用但你的性能特征明显不同,你可以旋转锁定,我希望这会损害可预测性。

在所有情况下,JVM是否足够智能以识别特定对象永远不会成为synchronized关键字的目标,从而避免创建互斥锁? 可以懒惰地创建互斥锁,但是这会引发一个自举需要互斥锁的自举问题,即使解决了这个问题,我也认为仍然会有一些开销用于跟踪是否已经创建了互斥锁。 所以我假设如果可以进行这样的优化,则必须在编译时或启动时完成。 在C ++中,由于编译模型这样的优化是不可能的(你不知道对象的锁是否会跨库边界使用),但是我对Java的编译和链接知道不够了解如果适用相同的限制。

作为一个看过一些JVM实现锁的方式的人说的话……

通常的方法是从对象的标题字中的几个保留位开始。 如果对象永远不会被锁定,或者它被锁定但是没有争用就会保持这种状态。 如果在锁定对象上发生争用,JVM 会将锁定膨胀为完整的互斥锁数据结构,并且在对象的生命周期内保持这种状态。

编辑 – 我刚刚注意到OP正在讨论操作系统支持的互斥锁。 在我看过的例子中,未充气的互斥体是使用CAS指令等直接实现的,而不是使用pthread库函数等。

这实际上是JVM的实现细节,不同的JVM可能以不同的方式实现它。 但是,由于Java在运行时链接,因此绝对不能在编译时进行优化,因此以前未知的代码可以获取在旧代码中创建的对象并开始在其上进行同步。

请注意,在Java术语中,同步原语称为“监视器”而不是互斥,并且它由特殊字节码操作支持。 这里有一个相当详细的解释。

你永远不能确定一个对象永远不会被用作锁(考虑reflection)。 通常,每个对象都有一个标题,其中一些位专用于锁。 可以实现它,以便只根据需要添加标头,但这有点复杂,你可能还需要一些标头(类(相当于“vtbl”和C ++中的分配大小),哈希代码和垃圾收集) 。

这是一个关于OpenJDK中同步实现的维基页面。

(在我看来,为每个对象添加锁是一个错误。)

JVM不能直接使用比较和交换指令吗? 假设每个对象都有一个字段lockingThreadId用于存储锁定它的线程的id,

 while( compare_and_swap (obj.lockingThreadId, null, thisThreadId) != thisTheadId ) // failed, someone else got it mark this thread as waiting on obj. shelf this thead //out of loop. now this thread locked the object do the work obj.lockingThreadId = null; wake up threads waiting on the obj 

这是一个玩具模型,但它似乎并不太昂贵,并且不依赖于操作系统。