4

考虑以下 Java 代码:

volatile boolean v1 = false;
volatile boolean v2 = false;

//Thread A
v1 = true;
if (v2)
    System.out.println("v2 was true");

//Thread B
v2 = true;
if (v1)
    System.out.println("v1 was true");

如果 volatile 访问有一个全局可见的总顺序,那么总是会达到至少一个 println。

Java 标准是否真的保证了这一点?或者这样的执行是否可能:

A: v1 = true;
B: v2 = true;
A: read v2 = false;
B: read v1 = false;
A: v2 = true becomes visible (after the if)
B: v1 = true becomes visible (after the if)

我只能在标准中找到关于访问同一volatile 变量的语句(但我可能会遗漏一些东西)。

“对 volatile 变量(第 8.3.1.4 节)v 的写入与任何线程对 v 的所有后续读取同步(其中后续是根据同步顺序定义的)。”

http://java.sun.com/docs/books/jls/third_edition/html/memory.html#17.4.4

谢谢!

4

6 回答 6

2

v1/v2 的 volatile read/write 这 4 个动作都是同步动作。执行对它们有一个同步顺序,这是一个总顺序。顺序必须保持每个线程的程序顺序。

这使得推理非常容易。显然,至少对于 2 个变量中的 1 个,对它的写入是在读取之前按“同步顺序”排序的。因此,写入与读取“同步”。因此,写入“发生在”读取之前。因此,写入对读取可见。

于 2010-03-31T22:54:16.973 回答
1

我认为这句话比你意识到的更能回答你的问题。

当线程尝试读取变量时,第 2 行中的线程写入v2被线程B“看到” 。A

我已将报价的关键部分加粗:

“对 volatile 变量(第 8.3.1.4 节)v 的写入与任何线程对 v 的所有后续读取同步......”

于 2010-03-31T18:35:07.207 回答
0

v2执行中的第三行是不可能的,因为volatile.

如果v2were not ,线程 A 可以看到 is stillvolatile的本地缓存副本。v2false

然而,由于v2是易失性的,每次读取都将直接进入主存储器,因此会看到最近写入的值。

也就是说,我不相信对不同全局变量的访问之间有任何特定的顺序保证,但我也不相信这对你的例子有影响。

于 2010-03-31T18:33:32.760 回答
0

Java 中的可变变量总是顺序一致的。因此它们的执行与所有易失性加载和存储的顺序执行是一致的。在这个顺序执行中,所有加载和存储都有一个总顺序(因此加载和存储在不同的地址上;不仅仅是在单个地址上)。

因此,您的示例中的执行是不允许的。

于 2021-03-18T04:58:58.527 回答
-1

好吧...根据 volatile 的定义,如果 B 将 v2 设置为 true,则 A 无法将其读取为 false。

volatile 使数据类型和引用在执行单个操作时按预期工作(所以 ++ 和 -- 不工作!)

于 2010-03-31T18:33:40.357 回答
-1

简而言之:至少会打印一次,因为这两个 volatile 操作无法重新排序。

我不知道它在规范中的什么地方,但在 Doug Lea 网站上有一个表,其中操作可以重新排序,哪些不能。( http://gee.cs.oswego.edu/dl/jmm/cookbook.html )


这种情况是不可能的,因为读取是在线程启动之前发生的初始化

错误的!

A: v1 = true;
B: v2 = true; 
A: read v2 = false; // initialization happens before thread start
B: read v1 = false; // initialization happens before thread start

如果仅在这种情况下,您可能会遇到这种情况,但这是不可能的,因为 v2 和 v1 是易失性的,并且写入 v1 必须在读取 v2 之前 并且写入 v2 必须在读取 v1 之前

错误的!

volatile boolean v1 = false;
volatile boolean v2 = false;

//Thread A
if (v2)    //read v2
   System.out.println("v2 was true");
v1 = true; //write to v1

//Thread B
if (v1)    //read v1
    System.out.println("v1 was true");
v2 = true; //write v2
于 2011-07-23T11:04:24.127 回答