在Does a correctly synchronized program still allow data race?(Part I)的讨论中,我们得到了两个非常好的例子。
我只是想讨论第二个问题。为方便起见,我在这里放了第二个例子:
public int hashCode() {
if (hash == 0 && count > 0) { //(1)
int h = hash;
int off = offset;
char val[] = value;
int len = count;
for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h; //(2)
}
return hash; //(3)
}
根据发生前关系定义中的第一项:如果x和y是同一个线程的动作,x在程序顺序中出现在y之前,那么hb(x,y),我们可能得出以下结论:
有hb((1),(2))和hb((2),(3)),因此hb((1),(3))。
由于:
hash是一个共享变量;
(1),(2),(3)是同一线程的所有动作;
(1)在(2)之前,(2)在程序顺序之前到达(3)。
现在回到我的问题:如果在(1)和(3)之间存在发生之前的关系,那么(1)和(3)永远不应该被重新排序。
我的解释是否存在任何误解?你的意见怎么样?
答案 0 :(得分:2)
对于从不同线程提交的(1),(2)和(3)中产生的动作没有强加顺序;唯一发生 - 在之前排序将在同一个线程提交的(1),(2),(3)的三元组内,如JLS 17.4.5所示:
如果x和y是同一个线程的动作,并且x在程序顺序中位于y之前,那么hb(x,y)。
机器指令的重新排序可能发生在任何执行中,只要执行它们的线程没有观察到与程序顺序不一致的排序。对于任何其他线程,他们可以按任何顺序观察操作(甚至根本不观察它们),因为发生在之前从这些操作发生到其他线程的任何操作。但是,您的示例仅包含一次写入,并且必须至少有两次写入才能赋予“乱序”概念以意义。只有写操作才能从其他线程中观察到效果。