java - volatile 是否提供其他正常存储并加载发生前的关系?
问题描述
我有一个关于 volatile 发生前规则的问题,同样的问题也符合监控规则。根据易失性规则,易失性写入发生在任何后续读取之前。
我有以下易失性写入和正常后续读取的示例。据我所知,这种易失性写入应该有一个 StoreStore 内存屏障,它将普通存储刷新到内存中,以便其他进程可以看到它(根据 DougLea 关于 JSR-133 内存模型的食谱)。
所以我的问题是:是否还有一个额外的发生前规则,即正常存储的操作 1 也发生在正常加载的后续操作 2 之前。
int a;
volatile int x;
public void action1(){
a = 100; --- normal store
x = 123; ---volatile store
}
public void action2{
int k = a; ---normal load
}
解决方案
是否还有一个额外的先发生规则,即正常存储的操作 1 也发生在正常加载的后续操作 2 之前?
不,它没有。
发生前发生在易失性写入和后续易失性读取之间。
在您的示例中,缺少易失性读取,因此没有将发生之前的关系链接到非易失性读取。因此,您的程序在内存可见性方面格式不正确。分配给的值k
可能不是100
, 在某些硬件等上。
要解决此问题,您需要执行以下操作:
int a;
volatile int x;
public void action1() {
a = 100; --- normal store
x = 123; ---volatile store
}
public void action2() {
int x = x; ---volatile load
int k = a; ---normal load
}
我还有一个问题,为什么 volatile 保证以下正常负载使用内存而不是缓存?(java规范没有解释底层,只说明规则,所以我不太了解背后的机制。
Java 规范故意不谈论硬件。相反,它指定了格式良好的 Java 程序的先决条件。如果程序满足这些先决条件,则可见性属性得到保证。 如何满足它们是编译器编写者的问题。
JMM 规范的一个结果是,在具有缓存和多处理器的硬件上,最明显和最有效的实现方法是进行缓存刷新等。但这是编译器编写者的关注点……而不是您的关注点。
您(Java 程序员)不需要了解缓存、内存屏障等。您只需要了解发生前发生的规则。但是,如果您想根据 JSR 133 食谱来理解事物,那么需要牢记以下几点:
食谱不是权威的,也不完整的。说的这么清楚。
Cookbook 仅与格式良好的程序的行为直接相关。如果所需的先发生链不存在,则可能会丢失必要的障碍,并且其他事情和所有赌注都将失败。
实际的 Java 实现不一定会按照 Cookbook ... umm ... 推荐的方式做事。
请注意,对于我的(更正)版本的示例,食谱说两个负载之间会有/应该有一个 LoadLoad 屏障。
推荐阅读
- python - 寻找最佳序列图解决方案
- python - 为同一对象的另一个实例更改“自我”?
- c# - 从 *.html 文件中指定 HTML 元素并在其中插入值
- javascript - req.user.roles = 未定义?
- apache-nifi - 删除 nifi 流文件内容的最快方法是什么?
- c# - 如何在 ToolTip 中绑定其他元素
- python - 如何检测python字符串中的多个关键字?
- xamarin.forms - 将 Xamarin 表单状态栏颜色更改为 iOS 的渐变色
- javascript - 为什么在 setTimeout 中输入 setState 会破坏中文输入法?
- php - 使用输入 MONTH 和 YEAR 过滤记录,但记录仍返回当前 MONTH 和 YEAR