我知道JVM内存模型是为cpu的最低公分母建立的,因此它必须假设JVM可以运行的cpu的最弱模型(例如ARM)。
现在,考虑到x64有一个相当强的内存模型,假设我知道我的程序只能在64位x86 CPU上运行,那么我可以忽略哪些同步实践?同样,当我的程序正在通过虚拟化运行时,这也适用吗?
示例:
众所周知,JVM的内存模型需要同步对longs和doubles的读/写访问,但可以假设其他32位原语(如int、float等)的读/写是原子的。
但是,如果我知道我正在64位x86机器上运行,我是否可以忽略使用longs/doubles上的锁,因为知道cpu会原子地读取/写入64位值,并且只会保持它们的易失性(就像我对ints/floats那样)?
发布于 2014-07-26 17:55:53
我知道JVM内存模型是为cpu的最低公分母建立的,因此它必须假设JVM可以运行的cpu的最弱模型(例如ARM)。
这不对。JMM产生于各种竞争力量之间的妥协:希望建立一个较弱的内存模型,以便程序能够在内存模型较弱的硬件上运行得更快;希望允许某些优化的编译器编写人员的愿望;以及希望并行Java程序的结果是正确和可预测的,如果可能的话(!)Java程序员可以理解。有关内存模型问题的一般概述,请参见Sarita Adve的CACM文章。
考虑到x64有一个相当强的内存模型,假设我知道我的程序将只在x64 CPU上运行,那么我可以忽略哪些同步实践?
没有。问题是,内存模型不仅适用于底层硬件,而且也适用于正在执行程序的JVM,而且在实践中主要适用于JVM的JIT编译器。编译器可能决定在内存模型中应用某些允许的优化,但是如果程序基于底层硬件对内存行为做出了不合理的假设,则程序将中断。
您询问了有关x64和原子64位写入的内容。这可能是因为在x64机器上永远不会发生单词撕裂。我怀疑任何JIT编译器是否会将64位的值作为优化而撕成32位的写操作,但您永远也不会知道。但是,您似乎不太可能使用此功能来避免程序中的同步或易失性字段。没有这些,对这些变量的写入可能永远不会在其他线程中可见,或者它们可能被任意地与其他写入重新排序,从而可能导致程序中的错误。
我的建议是,首先要正确地应用同步,以使程序正确。你可能会感到惊喜。同步操作经过了很大程度的优化,在普通情况下可以非常快。如果您发现存在瓶颈,请考虑使用优化,如锁拆分、挥发物的使用或转换为非阻塞算法。
更新
OP更新了有关使用volatile而不是锁和同步的问题。
事实证明,volatile不仅具有内存可见性语义。它还使long和double访问原子化,而这些类型的非volatile变量则不然。见JLS第17.7款。您应该能够依靠volatile在任何硬件上提供原子性,而不仅仅是x64。
在此期间,有关Java模型的其他信息,请参阅Aleksey的JMM语用谈话谈话全文。(阿列克西也是JMH的人。)在这个演讲中有很多细节,还有一些有趣的练习来测试一个人的理解。这篇演讲的一个总体观点是,依赖于一个人对内存模型如何工作的直觉,例如在缓存行或写缓冲区方面,往往是一个错误。JMM是一种关于内存操作和各种约束的形式主义(同步,与,发生,等等)决定了这些操作的顺序。这可能会有非常违反直觉的结果。试图通过考虑特定的硬件属性来超越JMM是不明智的。它会回来咬你的。
发布于 2014-07-23 18:38:43
您仍然需要处理线程安全,因此波动性语义和内存围栏仍然很重要。
我的意思是,例如在Oracle中,大多数低级别同步操作都以不安全的(docjar.com/docs/api/sun/misc/Unsafe.html#getUnsafe),结束,而后者又有大量的本地方法。因此,这些同步实践和许多其他低级操作最终都由它们所属的JVM封装。x64的jvm与x86不同。
在再次阅读您编辑的问题之后:加载/存储操作的原子性是一个主题这里。因此,不,您不必担心x64上的原子64位加载/存储。但是,由于这不是所有同步问题的结束,请参阅其他答案。
发布于 2014-08-01 08:51:30
总是包括JVM内存模型声明需要它们的内存屏障,然后允许JVM针对不同的平台进行优化。
知道只在x86 CPU上运行并不意味着可以使用内存屏障。除非您知道您将只运行在单核x86 cpus上;),在当今的多核世界中,没有人真正知道这一点。
为什么?因为java内存模型有两个主要关注点。
在没有内存障碍的情况下,其他内核可以看到的操作顺序可能会变得非常混乱;即使使用x86提供的更强的保证也是如此。x86只在数据到达cpu缓存时才能确保一致性,虽然其排序保证非常强,但它们只在Hotspot告诉CPU将其写入缓存之后才会启动。
如果没有易失性/同步,那么编译器(javac和hotspot)将决定何时进行这些写入,以及按什么顺序进行写入。它们决定将数据长时间保存在登记册内是完全有效的。一旦跨越了易失性或同步的内存屏障,JVM就知道如何告诉CPU将数据发送到缓存。
由于Doug文档在JSR-133烹饪本中,大多数x86屏障被简化为无操作指令,以保证排序。因此,JVM将使我们尽可能高效地执行指令。代码到Java内存模型,让Hotspot发挥它的魔力。如果Hotspot可以证明同步是不需要的,它可以完全放弃它。
最后,在多核x86上也证明了双重检查锁定模式被打破,尽管它具有更强的内存保证。这方面的一些很好的细节是由Bartos Milewski在他的C++ 博客上写的,这次是专门针对Java 这里的。
https://stackoverflow.com/questions/24918252
复制相似问题