【问题标题】:Does a correctly synchronized program still allow data race?(Part I)正确同步的程序是否仍然允许数据竞争?(第一部分)
【发布时间】:2012-08-17 13:26:06
【问题描述】:

JLS 有两个结论:

  • C1:如果程序没有数据争用,则程序的所有执行将显示为顺序一致:data-race-free => sequentially consistent
  • C2:如果程序正确同步,则程序的所有执行将显示为顺序一致:correctly synchronized => sequentially consistent

如果C1的反义词为真,那么我们可以得出结论:

  • C3:如果程序正确同步,则不会出现数据争用:correctly synchronized => data-race-free

但很遗憾,JLS 中并没有这样的说法,所以我得出第四个结论:

  • C4:程序可以正确同步并存在数据竞争。

但我对这种方法并不满意,我想证明这个结论是正确的(或错误的),即使是以非正式的方式或样本的方式。

首先,我认为显示包含数据竞争的多线程程序的顺序一致执行的代码段有助于理解和解决这个问题。

经过认真考虑,我仍然找不到合适的样品。那么请给我这样的代码段好吗?

【问题讨论】:

  • 就我个人而言,我会说 C3 是对 C2 的改写。我认为对于这种情况,代码示例可能没有太大帮助。您可以查看正确同步的代码示例,以证明同步代码将消除任何竞争条件。但是要同步的内容和原因的示例将特定于该示例。我建议阅读更多关于一般同步和多线程的内容。然后,您可以将这些概念应用到 Java 中。
  • 你可能会觉得this paper很有趣。
  • @newman 我已经改写了您的问题 - 如果您不喜欢我的编辑,请随时回滚。

标签: java multithreading memory-model


【解决方案1】:

String 的哈希码就是一个很好的例子:

private int hash; // Default to 0

public int hashCode() {
    int h = hash;
    if (h == 0 && count > 0) {
        int off = offset;
        char val[] = value;
        int len = count;

        for (int i = 0; i < len; i++) {
            h = 31*h + val[off++];
        }
        hash = h;
    }
    return h;
}

这里存在数据竞争,因为哈希可以由不同的线程写入和读取,并且没有发生前的关系(没有同步)。

但是程序是顺序一致的,因为线程不可能看到不是字符串实际hashcode的hashcode(当线程执行hashcode方法时,它要么看到0并重新计算值,即确定性,或者它看到一个有效值)。这是有效的,因为 int 写入是原子的。

编辑

这个(几乎)相同的代码被破坏并且可能返回 0 的哈希码:

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;
    }
    return hash; //(2)
}

因为 (1) 和 (2) 可以重新排序:(1) 可以读取非空值,而 (2) 将读取 0。这在第一个示例中不会发生,因为计算是在局部变量上进行的并且返回值也是那个局部变量,根据定义,它是线程安全的。

编辑 2

关于你的提议 C4,I don't think it is possible

当且仅当所有顺序一致的执行都没有数据竞争时,程序才能正确同步。

如果程序正确同步,则程序的所有执行都将显示为顺序一致(第 17.4.3 节)。

所以如果一个程序正确同步:

  • 所有的执行都显示顺序一致。
  • 所有顺序一致的执行都没有数据争用

因此我们可以得出结论,所有执行都没有数据竞争,因此程序没有数据竞争。

【讨论】:

  • +1 这实际上有一个术语。它被称为良性数据竞赛
  • @JohnVint 是的 - 感谢您的精确。我链接到的博客很有趣 - 将条件更改为 if(hash == 0) 会破坏代码...除非您是消防员,否则不要玩火!
  • @newman 这是一个全新的问题——我建议你这样问+你需要提供更多的背景信息。
  • RE "显示...的代码段" 这里给 OP 的主要信息应该是 没有像 代码段 这样的东西显示任何类型的执行,顺序一致与否。顺序一致性与代码正交:任何代码都可以以顺序一致和不一致的方式执行,这方面不受代码控制,而是受 Java 运行时实现的控制。这对 OP 很重要,因为他试图了解 JMM 使用这些术语的方式。
  • 我只是想帮助 OP,因为他的知识处于非常微妙的阶段,因此 100% 清晰的术语至关重要。请注意您的“编辑 2:因此,如果程序正确同步:所有执行都是顺序一致的。”事实上,执行很可能是不一致的;但会看起来是一致的。这是 OP 的一个非常重要的区别。此外,程序不是可以包含数据竞争的实体:它是该程序的 执行 的属性。这对于 OP 来说也非常重要,以便与 JMM 中的其他定义相匹配。
【解决方案2】:

竞争条件意味着让程序的输出取决于谁先到达特定点。例如,如果您有 2 个线程:T1 和 T2,如果您的程序输出是 X,如果 T1 首先到达程序中的点 P,并且如果程序的输出是 Y,如果 T2 先到达点 P,那么您就有竞争条件。

在伪代码中:

 Global variable i initialized to 6;
 Thread 1: 
       aquire(lock l)
             increment global variable i, i.e. i++;


 Thread 2: 
       aquire(lock l)
             double the value of global var i, i.e.: i*=2;

如果 T1 首先获得锁 l 并且 T2 第二次获得锁,则 i 的值将是 14 如果 T2 获得锁 l 第一和 T1 第二个,则 i 的值将是 13

【讨论】:

  • 无数据竞争意味着顺序一致性,反之则不然。
  • @Georghe 您的示例没有数据竞争,因为它使用了一种同步形式。
  • @assylias:程序的输出取决于哪个线程首先获得锁。这不是比赛条件吗?
  • JLS 定义:“当一个程序包含两个冲突的访问,这些访问没有按发生前的关系排序时,就说它包含数据竞争。” 在您的情况下,两个锁获取之间存在发生前的关系。显然,一个可以先于另一个成功,但这并不意味着它是一场数据竞赛。
  • @Gheorghe 我认为问题出在定义上。当您谈论竞争条件时,纽曼专门谈论数据竞争。这是 2 个不同的概念,因此会造成混淆。
【解决方案3】:

http://rsim.cs.illinois.edu/Pubs/popl05.pdf

证明似乎是这样的:

假设一个正确同步的程序的执行包含数据竞争,我们可以发现一个包含数据竞争的顺序执行,违反了“正确同步的程序”的定义[C1]

因此执行不能包含数据竞争。 [C3] 从那里,可以证明执行是顺序一致的。 [C2]

所以C3为真,用来证明C2。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多