多线程Java>volatile和final:volatile作为flushingallmemorycontent
请看下面的答案(1):
https://stackoverflow.com/a/2964277/2182302(Java Concurrency : Volatile vs final in "cascaded" variables?)
关于我的老问题(2):
one java memoryFlushing volatile: A good programdesign?
据我所知(见(2)),我可以使用volatile变量作为所有内存内容的内存屏障/刷新器,而不仅仅是volatile关键字引用的内容
现在(1)中被接受的答案是,它只会刷新连接易失性keyowrd的内存
那么现在什么是正确的呢?,如果(2)中的全部刷新原理是正确的,为什么我不能将volatile与final结合起来附加到变量上
# 1 楼答案
两个答案都不正确,因为你的想法是错误的。“刷新内存”的概念很简单。它不在Java虚拟机规范中。这不是一件事。是的,许多CPU/体系结构确实是这样工作的,但JVM却不是这样
你需要按照JVM规范进行编程。如果不能做到这一点,意味着你每次编写的代码都能在你的机器上正常工作,然后你把它上传到你的服务器上,结果它就失败了。这是一个可怕的场景:有错误的代码,但测试永远无法触发的错误。Yowza,那些很糟糕
那么,JVM规范中的是什么
不是“冲洗”的概念。它确实有HBHA的概念:发生在之前/之后。下面是它的工作原理:
例如:
在上面的例子中,在修改a/b状态的线程1和读取它们的线程2之间没有建立HB/HA关系
因此,JVM打印
20 0
是合法的,尽管这不能用基本的刷新概念来解释:JVM“刷新”b是合法的,而不是a你不太可能写出这段代码,并在任何JVM版本或任何硬件上实际观察到20/0打印,但重点是:它是允许的,有一天(或者可能已经存在),JVM+硬件+操作系统版本+机器状态的某种奇特组合会真正实现这一点,因此,如果您的代码在这一系列事件发生时中断,那么您编写了一个bug
实际上,如果一行变异了状态,而另一行读取了状态,而这两行没有HB/HA,你就搞砸了,你需要修复错误。甚至(尤其是!)如果你不能写一个测试来证明这一点
这里的诀窍是volatile readsdo建立HB/HA,而这是JVM规范必须同步内容的唯一机制,是的,这可以保证您“看到所有更改”。但这根本不是一个好主意。特别是因为JVM还表示hotspot编译器可以自由删除没有副作用的行
因此,现在我们必须就“建立HBHA”是否是副作用展开辩论。可能是这样,但现在我们来讨论优化规则:
编写惯用代码
每当azul、openjdk核心开发团队等考虑改进热点编译器的大量优化功能时,他们都会查看实际代码。这就像一个巨大的模式匹配器:他们在代码中寻找模式并找到优化它们的方法。他们不只是为所有可以想象的东西编写检测器:他们强烈地预测为实际java代码中常见的模式编写优化器。毕竟,花时间和精力优化一个几乎没有java代码实际包含的构造,有什么可能呢
这就引出了使用一次性易变读作为建立HB/HA的一种方法的基本问题:没有人这样做,因此,在某个时刻JVM被更新的几率(或者简单地说,冲突规则被“解释”为含义:是的,hotspot可以消除无意义的读取,即使它确实建立了一个现在不再存在的HB/HA)相当高——如果你以独特的方式做事,你也更有可能遇到JVM错误。毕竟,如果你做事情的方式很好,那么这个错误早就被报告并修复了
如何建立HB/HA:
自然规律是:在一个线程中,除了顺序之外,不能观察到代码以任何方式运行,即在一个线程中,所有行都以明显的方式彼此具有HB/HA
同步块:如果一个线程退出同步块,然后另一个线程在同一引用上进入一个,那么a中的同步块退出发生在同步块进入B之前
易失性读写
一些奇特的东西,比如:
thread.start()
发生在线程的run()方法的第一行之前,或者线程中的所有代码都保证在该线程的thread.yield()
完成之前被释放。这些往往是显而易见的因此,要回答这个问题,它是好的编程设计吗
不,不是
以正确的方式建立HB/HA:在^{中找到合适的东西并使用它。从简单锁到队列,再到整个作业的fork/join池。或者,停止共享状态。或者,与设计用于以比HB/HA更自然的方式进行并发访问的机制共享状态,例如数据库(事务)或消息队列