【问题标题】:Example for a correctly synchronized program with data races in Java memory modelJava 内存模型中具有数据竞争的正确同步程序示例
【发布时间】:2012-08-19 03:47:12
【问题描述】:

JLS, §17.4.5. Happens-before Order,它说

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

根据Does a correctly synchronized program still allow data race?(Part I)的讨论,我们得出以下结论:

程序可以正确同步并有数据竞争。

两个结论的结合意味着它一定存在这样一个例子:

程序的所有顺序一致执行都没有数据竞争,但此类程序的正常执行(除顺序一致执行之外的执行)包含数据竞争。

经过深思熟虑,我仍然找不到这样的代码示例。那你呢?

【问题讨论】:

标签: java memory-model jls


【解决方案1】:

“程序可以正确同步并存在数据竞争”的说法是不正确的。 assylias 在该讨论中的示例没有正确同步。从更高级别的功能角度来看,它是正确的——它包含的数据竞争并不表现为错误。这是一场所谓的“良性”数据竞赛,但在讨论 JLS 定义时这无关紧要。

保证顺序一致执行不包含数据竞争的程序在任何执行中都不包含数据竞争,无论是否顺序一致。正如 JLS 所说,

这对程序员来说是一个极强的保证。程序员不需要推理重新排序来确定他们的代码包含数据竞争。因此,在确定他们的代码是否正确同步时,他们不需要考虑重新排序。一旦确定代码已正确同步,程序员就不必担心重新排序会影响他或她的代码。

所以请注意,正确同步程序的定义被缩小为仅顺序一致的执行作为对程序员的礼貌,这给了他强有力的保证,即顺序一致的执行是他唯一的执行否则她需要推理,所有其他处决将自动获得相同的保证。

更新

JMM 使用的术语很容易迷失方向,细微的误解会导致日后产生深刻的误解。因此,请牢记这些:

  • 执行只是一组线程间操作。它没有先验顺序。特别是,它没有时间顺序

这是一个违反直觉的定义,所以我们必须小心:每次我们说执行,我们必须确保想象一个动作包,而不是一个他们的字符串。每当我们定义部分订单时,我们应该想象几个袋子排成一列

  • 程序包含执行操作的指令。每条此类指令可以执行零次或多次,为特定执行贡献零次或多次不同的操作;
  • 一个执行可能有也可能没有有一个执行顺序,它是一个所有动作的总顺序 ;
  • 顺序一致执行是如果您的所有共享变量都是易失的,您会得到什么。这种执行总是有一定的执行顺序;
  • 顺序不一致执行是程序的实际执行:涉及非易失性变量,编译器重新排序读取和写入,有缓存,线程-本地商店等。
  • 同步顺序是执行完成的所有同步操作的总顺序。就执行本身而言,它仍然是部分顺序,因为并非所有动作都是同步动作;最值得注意的是,非易失性变量的读取和写入。每次执行,无论是否顺序一致,都有明确的同步顺序;
  • 同样,happens-before 顺序是为程序的特定执行定义的,并作为同步顺序与程序顺序的传递闭包派生而来。

有趣的是,如果您的所有共享变量都是可变的,那么同步顺序将成为一个总顺序,因此符合执行顺序的定义。通过这种方式,我们从不同的角度得出这样的结论:此类程序的所有执行都顺序一致

我已经深入挖掘了数据竞赛定义中 JLS 错误的根源:

“当一个程序包含两个冲突的访问(第 17.4.1 节),它们不是按 happens-before 关系排序的,则称为包含数据竞争。”

首先,包含数据竞争的不是程序,而是程序执行。如果我们回头参考定义 Java 内存模型的 original paper,我们会看到这一点得到纠正:

“如果两个访问 xy 来自不同的线程,它们会在程序的执行中形成数据竞争,它们会发生冲突,并且它们没有按 发生在之前。”

但是,这仍然给我们留下了对 volatile var 的操作被定义为数据竞争。考虑以下 happens-before 图:

Thread W        w1 ----> w2
                |
                 \
Thread R     r0 ----> r1

r1 观察了写入 w1。它之前是另一次读取,r0,写入之后是另一次,w2。现在注意到 r0w1w2 之间没有路径; r1w2 之间也是如此。根据定义,所有这些都是数据竞争的示例。

然而,深入挖掘,我发现了this post on the memoryModel mailing list。它说“一个数据 比赛应该被定义为对非易失性变量的冲突操作 不是由happens-before排序的。只有这样添加才能关闭漏洞,但这仍然没有进入官方JLS发布。

【讨论】:

  • 如果“一个程序可以正确同步并且有数据竞争。”不是真的,那么我们得到“如果程序正确同步,那么它就没有数据竞争”。但从JLS,我们无法得出这样的结论。换句话说,如果第二个结论为真,那么 JLS 会向我们展示,但 JLS 不会。
  • “当一个程序包含两个冲突的访问(第 17.4.1 节),它们不是按 happens-before 关系排序的,则称为包含数据竞争。”请注意,happens-before 不是为程序定义的,而是为其执行 之一定义的。这只是 JLS 的一个软弱的措辞。这句话实际上是指一个程序执行。接下来请注意,您可以按照自己喜欢的方式重新排列执行顺序,而不会破坏 happens-before 关系。因此,您不能通过将顺序一致的执行重新排序为不一致的执行来引入数据竞争。
  • 关于您的回复,我有两点:1.您仍然没有回答我的问题,为什么 JLS 不说 “如果程序正确同步,则它没有数据竞争”。 2. 重新排列执行顺序任何方式都可能会破坏happens-before关系,如果您重新排列具有happens-before关系的动作的执行顺序。
  • @assylias 这就是我们通常假设的概念,但是 OP 转到 JLS 并读取“当且仅当所有顺序 一致 执行都是免费的时,程序才能正确同步数据竞赛。”并开始烦恼“但是顺序不一致的执行呢?”注意,我认为 OP 在允许自己使用快捷方式之前让条款 100% 明确确实是一个好方法。
  • @assylias 是的,特别是因为它是一个公认的 JLS 错误。此外,在这种精度级别之外的任何上下文中,“顺序一致”与“呈现顺序一致性”是同义词。由于永远无法获得实际的顺序一致性,这为该术语的有用含义创造了一种空缺。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-23
  • 2012-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多