首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >JMM:为什么这个结果是非法的?

JMM:为什么这个结果是非法的?
EN

Stack Overflow用户
提问于 2022-09-10 11:32:38
回答 1查看 67关注 0票数 3

我最近偶然发现了强应力中的例子

代码语言:javascript
复制
@JCStressTest
@State
@Outcome(id = "10",                      expect = ACCEPTABLE,             desc = "Boring")
@Outcome(id = {"0", "1"},                expect = FORBIDDEN,              desc = "Boring")
@Outcome(id = {"9", "8", "7", "6", "5"}, expect = ACCEPTABLE,             desc = "Okay")
@Outcome(                                expect = ACCEPTABLE_INTERESTING, desc = "Whoa")
public static class Volatiles {
    volatile int x;

    @Actor
    void actor1() {
        for (int i = 0; i < 5; i++) {
            x++;
        }
    }

    @Actor
    void actor2() {
        for (int i = 0; i < 5; i++) {
            x++;
        }
    }

    @Arbiter
    public void arbiter(I_Result r) {
        r.r1 = x;
    }
}

作者强调,由于增量不是原子操作,因此不能期望每次循环迭代“丢失”一个增量更新。因此,所有的结果(直到10),除了01,都是允许的(而且确实会发生)。

我明白了为什么不允许0:在初始化对象的默认值和每个线程中的第一个动作之间有一个HB边,如JLS中所述。JLS 17.4.4

对每个变量的默认值(零、假或空)的写入与每个线程中的第一个操作同步。

作者还解释了如何获得结果2

代码语言:javascript
复制
The most interesting result, "2" can be explained by this interleaving:
        Thread 1: (0 ------ stalled -------> 1)     (1->2)(2->3)(3->4)(4->5)
        Thread 2:   (0->1)(1->2)(2->3)(3->4)    (1 -------- stalled ---------> 2)

我知道你不能这样“扩展”上面的解释:

代码语言:javascript
复制
        Thread 1: (0 --------- stalled -----------> 1)     (1->2)(2->3)(3->4)(4->5)
        Thread 2:   (0->1)(1->2)(2->3)(3->4)(4->5)

因为它会导致结果5。但是,难道没有一种将产生1的执行吗?我绝对想不出任何东西。但为什么真的没有呢?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-09-11 22:08:25

首先,让我们记住volatile是如何在Java中工作的。

在Java中,所有易失性读和写都以运行时的全局顺序进行(即JLS 17.4.4中的同步顺序)。

这个全局顺序的属性:

  • 它保留了每个线程的易失性读写顺序(就JLS 17.4.4而言,它与程序顺序一致)。
  • 每执行一次:来自不同线程的操作可以在同一程序的不同执行中以不同的方式交织

易失性读取总是返回对此变量(即JLS 17.4.4中的同步)的最后一个(按此全局顺序)易失性写入。

其次,让我们澄清“增量不是原子操作”意味着x++由3个原子操作组成:

代码语言:javascript
复制
var temp = x;     // volatile read of 'x' to local variable 'temp'
temp = temp + 1;  // increment of local variable 'temp'
x = temp;         // volatile write to `x`

最后,让我们重写

代码语言:javascript
复制
The most interesting result, "2" can be explained by this interleaving:
        Thread 1: (0 ------ stalled -------> 1)     (1->2)(2->3)(3->4)(4->5)
        Thread 2:   (0->1)(1->2)(2->3)(3->4)    (1 -------- stalled ---------> 2)

以显示易失性读和写到x的方式

代码语言:javascript
复制
Thread1    Thread2
r₁₀:0                  | global
            r₂₀:0      | order of
            w₂₁:1      | volatile
            r₂₂:1      | reads
            w₂₃:2      | and
            r₂₄:2      | writes
            w₂₅:3      V
            r₂₆:3
            w₂₇:4
w₁₁:1
            r₂₈:1
r₁₂:1
w₁₃:2
r₁₄:2
w₁₅:3
r₁₆:3
w₁₇:4
r₁₈:4
w₁₉:5
            w₂₉:2

在这个图表上:

  • 易失性读写的全局顺序自上而下。
  • r:1x的易失读,它返回1
  • w:11x的易失性写入

请注意:

  • w总是在同一个线程中写入前一个r的值,该线程的增量为1。
  • r总是以全局顺序返回由前一个w编写的值。

现在我们可以回答你的问题了:

但是,难道没有一种将产生1的执行吗?我绝对想不出任何东西。但为什么真的没有呢?

  1. 查看全局顺序w₂₉中的最后一次写入:它总是写入(r₂₈读取的值)+1 (顺便说一下,全局顺序中的最后一次写总是Thread1中的最后一次写w₂₉,或者是Thread2中的最后一次写w₂₉;在本例中是w₂₉,但是对于w₁₉来说,推理是一样的)
  2. r₂₈总是按照全局顺序读取前面的写,即:
代码语言:javascript
复制
- either `w₂₇` from the same thread
- or some write `w₁ₓ` from Thread1 (if `w₁ₓ` happens in between `w₂₇` and `r₂₈` in runtime)

在任何情况下,r₂₈总是返回一些以前的非初始写。

但是每个非初始的写总是至少写1

这意味着w₂₉总是至少编写2

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73671449

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档