poq*_*poq 8 java final java-memory-model jls happens-before
内存模型在 17.4 中定义。内存模型。
17.5 中给出了现场final多线程保证。最终字段语义。
我不明白为什么这些是单独的部分。
AFAIKfinal和内存模型都提供了一些保证。
任何真正的程序执行都必须遵守这两个保证。
但现在很清楚这些final保证是否适用于用于验证 17.4.8 中因果关系要求的中间执行。执行和因果关系要求。
另一个不清楚的时刻是17.5.1。Final Fields 的语义定义了一个新的“special” ,它与内存模型happens-before中的不同:happens-before
此happens-before排序不会与其他happens-before排序传递地关闭。
如果它们相同happens-before,则happens-before不再是偏序(因为它不具有传递性)。
我不明白这怎么不会破坏事情。
如果这些不同happens-before,那么就不清楚 17.5 中的是什么。Final Field Semantics确实如此。17.4中
的。内存模型用于限制读取可以返回的内容:happens-before
非正式地,如果没有happens-before排序来阻止
r读取,则允许读取查看写入的结果。w
但是17.5。最后的字段语义是一个不同的部分。
rzw*_*oot 10
特殊的“最终场保证”部分是后来添加的。文档有时会遵循历史的怪癖 - 如果在 JMM 首次发布之前发现“最终现场保证”问题,文档的结构可能会有所不同。
\n换句话说,你问“为什么这个东西在一个单独的章节中”,也许答案是:“因为它是在 java 的更高版本中添加的,因此它是在完全不同的时间编写的;一个新的章节大概是添加更多文档的最简单方法”。当然,我们此时谈论的是几十年前。
\n\xc2\xa717.5 解释了其用途。引用:
\n\n\nFinal 字段的使用模型很简单:在对象的构造函数中设置该对象的 Final 字段;并且不要在对象的构造函数完成之前将对正在构造的对象的引用写入另一个线程可以看到它的地方。如果遵循这一点,那么当另一个线程看到该对象时,该线程将始终看到该对象的最终字段的正确构造版本。它还将看到这些最终字段引用的任何对象或数组的版本,这些版本至少与最终字段一样最新。
\n
换句话说,在遥远的过去,你可以这样做:
\n线程A:
\n线程B:
\nfinal未初始化的标记字段,因为初始化确实发生在线程 A 中,但不存在发生之前关系,并且重新排序和其他恶作剧意味着该线程还没有看到它。这非常烦人。不可变类的部分要点是您或多或少可以打印出 JMM 并将其点燃。如果您的系统是不可变类型的合并,那么您几乎不需要关心其中的每一个棘手规则。不过,在 \xc2\xa717.5 存在之前,它实际上并不是这样工作的
\nJMM 作为一般原则旨在为任何 JVM 实现提供尽可能少的“手铐”,同时使 JVM 的开发尽可能简单。这是一条很好的路线 - 例如,如果 JMM 简单地声明:“JVM 可以随时自由地重新排序它想要的任何内容,并在任何时间、任何它想要的持续时间内缓存它想要的任何内容”,然后编写 JVM根据规范快速运行代码会“更容易”(JVM 实现会更快),但是,编写实际执行您想要的操作的多线程代码变得几乎不可能。另一方面,JMM 还可以保证无论环境或体系结构如何,都无法观察到 JVM 中的重新排序。但这样的话 JVM 就会像糖蜜一样慢,参见 Python 及其饱受诟病的全局解释器锁。
\nJMM 试图成为愉快的妥协者。而\xc2\xa717.5也是抱着同样的精神来写的。
\n它基本上说:
\nfinal字段会正常运行,而不必担心发生在关系之前的任何情况。JMM 为 JVM 实现提供了操作空间。JVM impl 是否实际使用它取决于 JVM 实现者。换句话说,JVM 实现者很可能决定通过使用与保证 \xc2\xa717.4 中的 HB 内容相同的锁定机制来实现 \xc2\xa717.5,因此您可以有效地应用“HB 关系”等属性是传递性的”。JMM 的部分目的是允许 JVM 实现采取一些完全不同的方法来确保它所规定的保证实际上是如何得到保证的。这是因为必须编写 JVM,以便它们能够在各种硬件上以与本机代码一样快的速度运行代码,同时仍然是一个并非不可能开发的目标平台。
\n简直就是走钢丝。这是 JMM 的主要根本解释,有时可能是迟钝且奇怪的。
\n[1] 一个“行为良好”的构造函数:
\nthis) 传递给其自身类之外的任何代码。| 归档时间: |
|
| 查看次数: |
181 次 |
| 最近记录: |