当前位置: 首页 > 知识库问答 >
问题:

为什么以及如何volatile意味着原子读/写?

曾元忠
2023-03-14

首先,我知道volatile不会使多个操作(如i++)成为原子操作。这个问题是关于单个读或写操作的。

我最初的理解是,volatile只是执行了一个内存屏障(即其他线程将能够看到更新的值)。

现在我已经注意到JLS第17.7节说volatile还提供了一个读取或写入原子。例如,给定两个线程,两个线程都将不同的值写入volatile long x,那么x最终将恰好表示其中一个值。

我很好奇这怎么可能。在一个32位系统上,如果两个线程并行地写入一个64位的位置,并且没有“适当的”同步(即某种锁),那么结果应该是可能的混叠。为了清楚起见,让我们使用一个例子,其中线程1将0L写入相同的64位内存位置,而线程2将-1L写入相同的64位内存位置。

T1 writes lower 32 bit
T2 writes lower 32 bit
T2 writes upper 32 bit
T1 writes upper 32 bit

结果可能是0x0000FFFFF,这是不理想的。volatile如何防止这种情况?

我在其他地方也读到过,这通常不会降低性能。怎么可能同步写操作而只对速度产生很小的影响呢?

共有1个答案

柴丰
2023-03-14

您所说的volatile仅强制了一个内存屏障(意思是,刷新处理器缓存)是错误的。它还暗示了volatile值的读写组合的happens-before关系。例如:

class Foo {
  volatile boolean x;
  boolean y;

  void qux() {
    x = true; // volatile write
    y = true;
  }

  void baz() {
    System.out.print(x); // volatile read
    System.out.print(" ");
    System.out.print(y);
  }
}

当您从两个线程运行这两个方法时,上面的代码将打印true falsetrue truefalse false,但永远不会打印false true。如果没有volatile关键字,您就不能保证后面的情况,因为JIT编译器可能会重新排序语句。

与JIT编译器能够保证这一条件的方式相同,is能够保护程序集中的64位值读写。volatile值由JIT编译器显式处理,以确保其原子性。一些处理器指令集直接通过特定的64位指令来支持这一点,否则JIT编译器会对其进行仿真。

JVM更复杂,正如您可能期望的那样,并且通常在没有完整范围的情况下解释它。请考虑阅读这篇涵盖所有细节的优秀文章。

 类似资料:
  • 我想创建一个

  • 问题内容: 我了解这是用来创建别名的。因此,将一个长名称别名为一个短名称是有意义的。但是,我看到一个查询 ,这意味着什么? 问题答案: 可以以多种方式使用别名,而不仅仅是缩短长列名。 在这种情况下,您的示例意味着您将返回一列始终包含的列,并且其别名/列名称为。 当您使用计算值(例如)时,也可以使用别名。

  • 问题内容: 原始浮点值怎么可能是?这意味着什么? 我可以取消该功能吗? 当我有: 然后返回,也是如此。但是当我打印时,它会打印。 问题答案: 因为Java使用IEEE浮点算术标准(IEEE 754)来定义-0.0和使用它。 可表示的最小数字在次正规有效位中没有1位,并且被称为正负零,由正负号确定。它实际上表示在零到相同符号的最小可表示非零数字之间的范围内将数字四舍五入为零,这就是为什么它有一个符号

  • ELIFECYCLE是什么意思? 我在寻找两个答案中的一个:ELIFECYCLE是什么意思?(或者)为什么我的应用程序代码中出现错误?

  • 问题内容: 下面是代码片段。 有人可以向我解释@符号在Java中的含义吗? 问题答案: 这是一个注释。 注释是元数据的一种形式。它们提供的程序数据不属于程序本身。注释对其注释的代码的操作没有直接影响。 注释确实会影响工具和库对待程序的方式,进而会影响正在运行的程序的语义。可以从源文件,类文件中读取注释,也可以在运行时从中读取注释。

  • 问题内容: 异步在Ajax中意味着什么?以及Ajax如何知道何时在不进行服务器轮询的情况下提取数据? 问题答案: 异步意味着脚本将向服务器发送请求,并继续执行它而无需等待答复。收到答复后,立即触发浏览器事件,该事件进而允许脚本执行关联的操作。 Ajax知道何时从服务器提取数据,因为您告诉它何时进行处理。