Java volatile是否读取刷新写入,并执行易失性写入更新读取

Apu*_*ngh 16 java multithreading

我理解read-acquire(在它之后没有重新排序后续的读/写操作)和write-release(不重新排序它之前的读/写操作).我的q是: -

  1. 在读取获取的情况下,它之前的写入是否被刷新?
  2. 在写入释放的情况下,先前的读取是否会更新?

读取和读取相同,与易失性读取相同,写入发布与Java中的易失性写入相同吗?

为什么这很重要的是,让我们来看看写释放的情况.

y = x; // a read.. let's say x is 1 at this point
System.out.println(y);// 1 printed
//or you can also consider System.out.println(x);
write_release_barrier();
//somewhere here, some thread sets x = 2
ready = true;// this is volatile
System.out.println(y);// or maybe, println(x).. what will be printed?
Run Code Online (Sandbox Code Playgroud)

此时,是x 2还是1?在这里,考虑准备挥发.据我所知,volatile之前的所有商店都将首先显示出来......然后只有volatile才能显示出来.谢谢.

参考: - http://preshing.com/20120913/acquire-and-release-semantics/

ysh*_*vit 11

否:并非所有写入都被刷新,所有读取都不会更新.

Java在多线程的"先发生"基础上工作.基本上,如果A发生在B之前,B发生在C之前,那么A发生在C之前.所以你的问题等于是否x=2正式发生 - 在一些读取x的动作之前.

Happens-before edge基本上是通过同步关系建立的,这些关系在JLS 17.4.4中定义.有几种不同的方法可以做到这一点,但对于易失性,它基本上等于对易失性事件的写入 - 在读取相同的易失性之前:

  • 对易失性变量v(第8.3.1.4节)的写入与任何线程对v的所有后续读取同步(其中"后续"根据同步顺序定义).

鉴于此,如果您的线程写入ready = true,那么单独写入并不意味着在它之前发生任何事情(就该写入而言).实际上恰恰相反; 写入ready发生在其他线程上的事情之前,如果他们阅读ready.

所以,如果另一个线程(那些集合x = 2)设置之后已经写好了x = 2,并且这个线程(你在上面发布)然后读取ready,那么它会看到x = 2.这是因为写入发生在读取之前,因此读取线程会看到写入线程已完成的所有操作(直到并包括写入).否则,你有一个数据竞争,基本上所有的赌注都是关闭的.

一些额外的说明:

  • 如果您没有事先发生的边缘,您可能仍会看到更新; 只是你不能保证.所以,不要假设如果你不读写ready,那么你仍然会看到x = 1.您可能会看到x = 1或x = 2,或者可能是其他一些写入(最多包括x = 0的默认值)
  • 在您的示例中,y始终为1,因为您不会x在"somewhere here"注释后重新阅读.出于这个答案的目的,我假设在y=x紧接之前或之后有第二行ready = true.如果没有,则y的值将与第一个中的值保持不变println(假设没有其他线程直接更改它 - 如果它是局部变量则保证),因为线程内的操作总是看起来好像它们不是重新排序.


Ste*_*n C 7

Java内存模型未在"读取 - 获取"和"写入释放"方面指定.这些术语/概念来自其他背景,正如您引用的文章非常清楚,它们经常被(由不同的专家)用来表示不同的事物.

如果你想了解挥发性物质在Java中是如何工作的,你需要理解Java内存模型和Java术语......(幸运的是)有充分理由并精确指定1.尝试将Java内存模型映射到"read-acquire"和"write-release"语义是一个坏主意,因为:

  • "读取 - 获取"和"写 - 释放"术语和语义没有明确规定,并且

  • 假设的JMM - >"读取 - 获取"/"写 - 释放"语义映射只是JMM的一种可能实现.其他映射可能存在不同的,同样有效的语义.


1 - ...模块专家已经注意到某些版本的JMM存在缺陷.但重点是,已经进行了认真的尝试,以提供理论上合理的规范......作为Java语言规范的一部分.


eri*_*son 5

不,读取volatile变量不会刷新先前的写入.可见操作将确保前面的操作可见,但读取volatile变量对其他线程不可见.

不,写入volatile变量不会清除先前读取值的缓存.它只能保证刷新以前的写入.

在你的例子中,y最后一行显然仍然是1.y根据前面的输出,只进行了一次分配,即1.也许这是一个错字,你打算写println(x),但即使这样,2的值也不能保证可见.