Java内存模型中具有数据争用的正确同步程序的示例

new*_*man 8 java memory-model jls

JLS中,§17.4.5.发生在秩序之前,它说

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

根据讨论,如果一个正确同步的程序仍允许数据竞争吗?(第一部分),我们得出以下结论:

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

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

程序的所有顺序一致的执行都是数据竞争的,但是这样的程序的正常执行(除了顺序一致的执行之外的执行)包含数据竞争.

经过深思熟虑,我还是找不到这样的代码样本.那你呢?

Mar*_*nik 6

"程序可以正确同步并进行数据竞争"是不正确的.该讨论中assylias的示例未正确同步.从更高级别的功能角度看它是正确的 - 它包含的数据竞争并不表现为错误.这是一种所谓的"良性"数据竞争,但在讨论JLS定义时这是无关紧要的.

保证顺序一致执行不包含数据争用的程序在任何执行中都不包含数据争用,顺序一致或不一致.正如JLS所说,

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

所以请注意,正确同步的程序的定义被缩小到只有顺序一致的执行,这是对程序员的礼貌,这使他有力地保证顺序一致的执行是他或她需要推理的唯一执行和所有其他执行将自动拥有相同的保证.

UPDATE

在JMM使用的术语中很容易迷失方向,微妙的误解会导致后来的误解.因此,请记住这些:

  • 执行就是一组线程间行动.没有先验订单.特别是,它没有时间顺序.

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

  • 一个程序包含的指令执行操作.每个这样的指令可以执行零次或多次,对特定执行贡献零个或多个不同的动作;
  • 的执行可以或可以不具有一个执行顺序,这是一个总次序对所有的行动;
  • 一个顺序一致的执行是如果所有的共享变量是挥发性的,你会得到什么.这种执行总是有一个明确的执行顺序;
  • 一个顺序不一致的执行是你的真实世界程序的执行:非易失性变量是参与和编译器重新排序的读取和写入,有高速缓存,线程局部店等
  • 同步顺序是所有的订单总额同步操作由执行完成.关于执行本身,它仍然是部分顺序,因为并非所有动作都是同步动作; 最值得注意的是,读取和写入非易失性变量.每次执行,无论是否顺序一致,都有一个明确的同步顺序;
  • 同样地,发生先前的顺序是为程序的特定执行定义的,并且被导出为具有程序顺序的同步顺序的传递闭包.

有趣的是,如果所有共享变量都是易失性的,那么同步顺序将变为总顺序,因此将满足执行顺序的定义.这样,我们从一个不同的角度得出结论,即这样一个程序的所有执行都是顺序一致的.

我深入研究了数据竞争定义中JLS漏洞的底部:

"当一个程序包含两个冲突访问(第17.4.1节)时,这些访问不是由先发生过的关系排序的,而是说它包含数据竞争."

首先,它不是程序包含数据的种族,而是一个程序执行.如果我们回顾一下定义Java内存模型的原始论文,我们会看到这个更正:

"两个访问xy在执行程序时会形成一个数据竞争,如果它们来自不同的线程,它们会发生冲突,并且它们不是按先发生的顺序排序的."

但是,这仍然使我们对易失性变量的行为被定义为数据竞争.考虑以下事件 - 在之前:

Thread W        w1 ----> w2
                |
                 \
Thread R     r0 ----> r1
Run Code Online (Sandbox Code Playgroud)

r1观察写w1.之前是另一个读取r0,写入之后是另一个读取,w2.现在注意到r0w1w2之间没有路径; 同样在r1w2之间.所有这些都是定义中数据竞争的例子.

然而,深入挖掘,我在memoryModel邮件列表上找到了这篇文章.它说"数据竞争应被定义为非易失性变量的冲突行为,这些变量不是按先发生顺序排序的".只有这样的补充才会关闭漏洞,但这仍然没有进入正式的JLS版本.