首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java中的StoreStore内存屏障是否禁止读写重排序?

Java中的StoreStore内存屏障是否禁止读写重排序?
EN

Stack Overflow用户
提问于 2019-10-01 13:00:57
回答 2查看 284关注 0票数 0

现在我们有了

代码语言:javascript
复制
Load A
StoreStore
Store B

是否有可能实际执行命令如下

代码语言:javascript
复制
StoreStore
Store B
Load A

如果可能的话,如何解释似乎违反了The Java volatile Happens-Before Guarantee的情况。

据我所知,易失性语义是使用以下JMM内存屏障添加策略实现的

代码语言:javascript
复制
insert a StoreStore before volatile variable write operation
insert a StoreLoad after volatile variable write operation
insert a LoadLoad after volatile variable read operation
insert a LoadStore after volatile variable read operation

现在,如果我们有两个java线程,如下所示

螺纹1

代码语言:javascript
复制
Load A
StoreStore
Store volatile B

螺纹2

代码语言:javascript
复制
Load volatile B
Load C

根据“Java易失性发生-在保证之前”,Load A应该发生在Load C之前,而Load volatile BStore volatile B之后,但是如果Load A可以被重新排序为“存储易失性B之后”,如何保证Load A is before Load C

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-10-01 14:25:38

从技术上讲,Java语言没有内存障碍。相反,Java内存模型是在关系发生之前指定的;有关详细信息,请参阅以下内容:

您正在讨论的术语来自于编者用的JSR-133烹饪本。正如文档所述,它是为编写实现Java内存模型的编译器的人员提供的指南。它解释了JMM的含义,显然并不打算成为正式规范。JLS是规范。

JSR-133食谱中关于内存屏障的部分根据它们约束特定的加载和存储序列的方式对它们进行分类。对于StoreStore障碍,它说:

序列:Store1; StoreStore; Store2确保在与Store2和所有后续存储指令相关联的数据之前,存储1的数据对其他处理器(即刷新到内存)是可见的。通常,在处理器上需要StoreStore屏障,否则不能保证从写缓冲区和/或缓存到其他处理器或主内存的刷新的严格顺序。

如您所见,StoreStore屏障只约束store操作的行为。

在您的示例中,您有一个load和一个storeStoreStore屏障的语义与load操作无关。因此,您提议的重新排序是允许的。

票数 1
EN

Stack Overflow用户

发布于 2019-10-12 04:31:40

这只是回答你问题的最新部分。

首先,您提供的示例不是Java代码。因此,我们不能将JMM推理应用于它。(只是为了让我们清楚地知道这一点。)

如果您想了解Java代码是如何运行的,请忘记内存障碍。Java内存模型告诉您所有需要做的事情,以便内存读写具有有保证的行为。以及你需要知道的一切,以便对(正确)行为进行推理。所以:

  • 编写Java代码
  • 分析代码,以确保在所有情况下,在线程上需要读取由另一个线程编写的值的情况下,在链之前都有正确的发生。
  • 将编译(正确) Java代码的问题留给编译器处理。

看看你的例子中的伪指令序列,它们没有多大意义。我不认为真正的Java编译器在编译真正的Java代码时会(在内部)使用类似的障碍。相反,我认为在每次易失性写入和每次易失性读取之前都会有一个StoreLoad内存屏障。

让我们考虑一些真正的Java代码片段:

代码语言:javascript
复制
public int a;
public volatile int b;

// thread "one"
{
  a = 1;
  b = 2;
}

// thread "two"
{ 
  if (b == 2) {
      print(a);
  }
}

现在假设线程“2”中的代码是在线程“1”之后执行的,就会发生这样的情况--在这样的链之前:

  • a = 1发生-在b = 2之前
  • b = 2发生-在b == 2之前
  • b == 2发生-在print(a)之前

除非涉及到其他代码,否则在链之前发生的情况意味着线程“2”将打印"1“。

注意:

  1. 没有必要考虑编译器在编译代码时使用的内存屏障。
  2. 这些障碍是特定于实现的,是编译器的内部障碍。
  3. 如果您查看本机代码,您将不会看到内存屏障本身。您将看到具有所需语义的本机指令,以确保存在(隐藏的)内存屏障。
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58185339

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档