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)永远不应重新排序。

我的解释中有什么误会吗?您的意见呢?

最佳答案

从不同线程提交的(1),(2)和(3)产生的动作没有顺序。根据JLS 17.4.5,唯一的事情是在排序之前发生在同一线程提交的(1),(2),(3)的三元组中:


  如果x和y是同一线程的动作,并且x按程序顺序位于y之前,则hb(x,y)。


只要执行的线程没有发现与程序顺序不一致的顺序,就可以在任何执行中对机器指令进行重新排序。对于任何其他线程,由于没有发生任何事情,它们可以按任何顺序观察动作(甚至根本不观察它们),然后再从这些动作转到其他线程的任何动作。但是,您的示例仅包含一次写入,并且至少必须进行两次写入才能使“乱序”的概念有意义。仅写操作具有其他线程可观察到的效果。

09-11 06:45