JVM是否为每个对象创建互斥体以实现';同步';关键词?如果没有,怎么做? 作为一个C++程序员变得更熟悉java,我看到对任意对象的语言级支持没有任何奇怪的声明,对象支持这样的锁定,这有点奇怪。为每个对象创建互斥对象似乎是自动选择的沉重代价。除了内存使用,互斥体在某些平台上是操作系统有限的资源。如果互斥锁不可用,您可以旋转锁,但是互斥锁的性能特征明显不同,我认为这会损害可预测性

JVM是否为每个对象创建互斥体以实现';同步';关键词?如果没有,怎么做? 作为一个C++程序员变得更熟悉java,我看到对任意对象的语言级支持没有任何奇怪的声明,对象支持这样的锁定,这有点奇怪。为每个对象创建互斥对象似乎是自动选择的沉重代价。除了内存使用,互斥体在某些平台上是操作系统有限的资源。如果互斥锁不可用,您可以旋转锁,但是互斥锁的性能特征明显不同,我认为这会损害可预测性,java,c++,synchronization,jvm,implementation,Java,C++,Synchronization,Jvm,Implementation,JVM是否在所有情况下都足够聪明,能够识别特定对象永远不会成为synchronized关键字的目标,从而避免创建互斥锁?互斥体可以惰性地创建,但这带来了一个自举问题,它本身就需要一个互斥体,即使已经解决了这个问题,我认为仍然会有一些开销来跟踪互斥体是否已经创建。所以我假设如果这样的优化是可能的,它必须在编译时或启动时完成。在C++中,这样的优化是不可能的,因为编译模型(你不知道对象的锁是否会被跨库边界使用),但是我对java的编译和链接了解不够,知道是否有同样的限制。 你永远不能确定一个对象永远

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

你永远不能确定一个对象永远不会被用作锁。(考虑反射)。通常每个对象都有一个头部,其中有一些位专用于锁。可以实现它,以便只在需要时添加头部,但这会变得有点复杂,并且您可能仍然需要一些头部(类(相当于C++中的“vtbl”和分配大小)、哈希代码和垃圾收集)


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

作为一个研究了一些JVM实现锁的方式的人来说

通常的方法是从对象头字中的两个保留位开始。如果对象从未被锁定,或者如果对象被锁定但没有争用,则保持这种方式。如果在锁定的对象上发生争用,JVM会将锁扩展为完整的互斥体数据结构,并且在生命周期中保持这种方式让我看看这个物体


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

这实际上是JVM的一个实现细节,不同的JVM可能会但是,它肯定不是可以在编译时优化的,因为Java在运行时链接,这使得以前未知的代码有可能获得在旧代码中创建的对象,并在其上开始同步


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

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

这是一个玩具模型,但它看起来不太贵,也不依赖操作系统。

有没有什么地方可以让我读到更多关于这个“头”的信息?既然你说你可能需要它,我想它是用于锁定以外的东西。它都在GPL/JRL/JIUL源代码中!没错,但我希望如果Java程序员对它感兴趣的话他回答了这些问题,我认为有人写了比数百万行源代码更容易理解的东西。如果我有足够的兴趣,有足够的时间,我最终可以做到;)Java程序员不感兴趣:)它是为懒惰的人设计的。只要您理解语义,并且程序运行得相当好,就不会有人关心引擎盖下的内容…很好的链接!它解释了为什么Java6的锁定速度要快得多。这正是我要找的!我很确定
synchronized
是在JVM内部处理的,而不是依赖操作系统级资源。这不可能完全正确。我同意它可以按照JVM想要的任何方式实现,但是JVM必须依赖于内部某个地方的操作系统级通信。你可以编写你自己的互斥锁类来旋转锁,但是如果你想让操作系统调度器让线程进入睡眠状态并适当地唤醒它们,那么在某个时候你必须告诉操作系统你的互斥锁,这些类型的注册是有限的资源。你不需要告诉操作系统你的互斥锁,你只需要告诉它什么时候需要唤醒某些线程。好的一点是,如果操作系统提供了一种方式让一个线程直接通知另一个线程恢复,JVM可以使用该功能。但是AFAIK应用程序总是通过信号量来实现这一功能。通过膨胀,你的意思是像C/C++中的小字符串优化这样的事情正在发生吗?()基本上,信息以字内的位开始,但当满足某些条件时,字开始被解释为指向具有更多字段的对象的指针?仅使用CAS指令,如何让操作系统调度程序使线程进入睡眠状态?@JosephGarvin-你没有。在未膨胀/未竞争的情况下,您只能使用CAS获取/释放锁。如果检测到争用,锁定/解锁代码将执行相关的线程调度系统调用。。。或者随便什么。允许锁定任何对象的设计选择可能是错误的。可能只允许某些类型的对象用于同步目的,就像只允许抛出某些类型的对象一样
synchronized(一个正常的对象){}
让人非常困惑