考虑以下Java程序:
Label
因为static volatile int shared;
public static void main(final String[] args) {
final Runnable r = () -> {
shared = 1;
};
new Thread(r).start();
new Thread(r).start();
}
被标记为shared
,所以我想说这个程序没有数据争用。但是,如何基于JLS(例如版本11)来激发这种动机?
在第17章中,我们被告知:
当程序包含两个冲突访问(第17.4.1节)时,它们之间没有按事前发生关系进行排序,则该程序被称为包含数据争用。
我认为这是JLS提供的数据竞争的定义。然后我们有17.4.1节告诉我们:
如果至少有一次访问是写操作,则对同一变量的两次访问(读或写)被认为是冲突的。
好的,所以我们在这里有冲突的访问,因为我们有两次写入volatile
的操作。现在,我们必须在两次写入之间具有先发生后发生的关系,否则我们将面临一场竞赛。但是,我还没有找到为什么我们在这里有这种关系。第17章告诉我:
如果动作x与后面的动作y同步,那么我们还有hb(x,y)。
同一章告诉我:
对易失性变量v(第8.3.1.4节)的写操作与任何线程对v的所有后续读取进行同步(其中“后续”根据同步顺序定义)。
但是在将写入与易失性变量相关联之前,我还没有发现任何事情发生。为什么会这样?
答案 0 :(得分:1)
对 volatile 变量的访问不会从不受到数据竞争的约束。
在JLS中不是很明显,但是在短语中
(第17.4.1节)如果至少有一次访问是写操作,则对同一变量的两次访问(读或写)被认为是冲突的。
术语“读”和“写”不是通用的,但在下一部分“ 17.4.2操作”中将其描述为对非易失性变量的访问:
•读取(正常或非易失性)。读取变量。
•写入(正常或非易失性)。写一个变量。
在该部分中,对 volatile 变量的访问被归类为同步操作的一部分,并用不同的术语“易读”和“易写”:
– 易读。易读变量。
– 易失性写。可变地写变量。
因为只有(非易失性)读取和写入可能会发生冲突,所以只有那些访问可能包含数据竞争。 易失性的读取和写入不会发生冲突,并且绝不会包含数据争用。