20

作为一个越来越熟悉 Java 的 C++ 程序员,看到语言级别支持锁定任意对象而没有任何类型的对象支持这种锁定的声明对我来说有点奇怪。为每个对象创建互斥锁似乎是自动选择加入的沉重代价。除了内存使用之外,互斥锁在某些平台上是操作系统受限的资源。如果互斥锁不可用,您可以自旋锁,但其性能特征明显不同,我预计这会损害可预测性。

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

4

4 回答 4

17

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

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

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

于 2009-12-14T00:46:52.677 回答
2

你永远不能确定一个对象永远不会被用作锁(考虑反射)。通常,每个对象都有一个标头,其中包含一些专用于锁的位。可以实现它,以便仅根据需要添加标头,但这有点复杂,并且您可能无论如何都需要一些标头(类(相当于“vtbl”和 C++ 中的分配大小)、哈希码和垃圾收集) .

这是关于在 OpenJDK 中实现同步的 wiki 页面。

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

于 2009-12-14T00:38:27.783 回答
2

这确实是JVM的一个实现细节,不同的JVM可能实现的不同。但是,它绝对不是可以在编译时优化的东西,因为 Java 在运行时链接,这使得以前未知的代码可以获取在旧代码中创建的对象并开始对其进行同步。

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

于 2009-12-14T00:47:34.987 回答
1

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

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

于 2009-12-14T01:25:50.263 回答