我正在阅读JLS的第17章。线程和锁定,其中关于Java中顺序一致性的以下说法似乎是不正确的:
如果程序没有数据竞争,则程序的所有执行将呈现出顺序一致性。
他们将数据竞争定义为:
当一个程序包含两个冲突访问(§17.4.1),这些访问未按happens-before关系排序时,就会发生数据竞争。
他们将冲突访问定义为:
对同一变量的两个访问(读取或写入)被称为冲突访问,如果其中至少一个访问是写入。
最后,他们关于happens-before关系有以下内容:
对易失字段(§8.3.1.4)的写入在随后对该字段的每次读取之前发生。
我对第一条语句的问题在于,我认为我可以想出一个Java程序,它没有数据竞争,但允许顺序不一致的执行:
在上面的代码中,我还展示了线程代码如何在运行时交错。因此,我理解,这个程序:
- 没有数据竞争:Thread2中对v1和v2的读取与Thread1中的写入同步。 - 可以输出“v1=1 v2=4”(违反了顺序一致性)。
因此,JLS的初始陈述
如果一个程序没有数据竞争,那么程序的所有执行都将呈现为顺序一致的。
对我来说似乎是不正确的。我是否遗漏了什么或者我犯了错误?
编辑:用户chrylis-cautiouslyoptimistic正确指出了我给出的代码可以按顺序一致的方式输出“v1=1 v2=4”——线程代码中的行只需要稍微交错一下即可。
因此,这是稍微修改后的代码(我改变了读取的顺序),顺序一致性无法输出“v1=1 v2=4”,但仍然适用于所有内容。
如果程序没有数据竞争,则程序的所有执行将呈现出顺序一致性。
他们将数据竞争定义为:
当一个程序包含两个冲突访问(§17.4.1),这些访问未按happens-before关系排序时,就会发生数据竞争。
他们将冲突访问定义为:
对同一变量的两个访问(读取或写入)被称为冲突访问,如果其中至少一个访问是写入。
最后,他们关于happens-before关系有以下内容:
对易失字段(§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;