【发布时间】:2021-01-01 04:12:20
【问题描述】:
我正在阅读Chapter 17. Threads and Locks of JLS,以下关于 Java 中顺序一致性的陈述对我来说似乎不正确:
如果一个程序没有数据竞争,那么该程序的所有执行将看起来是顺序一致的。
他们将数据竞赛定义为:
当一个程序包含两个冲突的访问(第 17.4.1 节)时,这些访问没有按发生前的关系排序,则称为包含数据竞争。
他们将冲突访问定义为:
如果至少有一次访问是写入,则对同一变量的两次访问(读取或写入)称为冲突。
最后他们有以下关于发生前的关系:
对 volatile 字段(第 8.3.1.4 节)的写入发生在对该字段的每次后续读取之前。
我对第一条语句的问题是我认为我可以想出一个没有数据竞争并且允许顺序不一致执行的 Java 程序:
// Shared code
volatile int vv = 0;
int v1 = 0;
int v2 = 0;
// Thread1 Thread2
v1 = 1;
v2 = 2;
vv = 10; while(vv == 0) {;}
int r1 = v1;
int r2 = v2;
System.out.println("v1=" + r1 + " v2=" + r2);
v1 = 3;
v2 = 4;
vv = 20;
在上面的代码中,我还用缩进方式展示了线程的代码是如何在运行时交错的。
所以,据我了解,这个程序:
- 没有数据争用:Thread2 中 v1 和 v2 的读取与 Thread1 中的写入同步
- 可以输出
v1=1 v2=4(这违反了顺序一致性)。
因此,JLS 的初始声明
如果一个程序没有数据竞争,那么该程序的所有执行将看起来是顺序一致的。
对我来说似乎不正确。
是我遗漏了什么还是我在某处犯了错误?
编辑: 用户chrylis-cautiouslyoptimistic 正确地指出我给出的代码可以输出具有顺序一致性的v1=1 v2=4 — 线程代码中的行只是应该交错一点点不同。
所以这里是稍微修改的代码(我已经改变了读取的顺序),其顺序一致性无法输出v1=1 v2=4,但一切仍然适用。
// Shared code
volatile int vv = 0;
int v1 = 0;
int v2 = 0;
// Thread1 Thread2
v1 = 1;
v2 = 2;
vv = 10; while(vv == 0) {;}
int r2 = v2;
int r1 = v1;
System.out.println("v1=" + r1 + " v2=" + r2);
v1 = 3;
v2 = 4;
vv = 20;
【问题讨论】:
-
你怎么知道线程的执行是如何在运行时交错的?
-
你的意思是在上面的代码示例中吗?我只是举了一种可能的执行方式作为例子。代码示例的重点在于它与 Java 语言规范相矛盾:它符合他们的“顺序一致”标准,但允许违反它的执行。
标签: java java-memory-model jls