发生在Java内存模型中的规则之前

时间:2018-01-27 14:45:11

标签: java multithreading concurrency java-memory-model happens-before

我目前正在攻读并发编程考试并且不明白为什么这个程序的输出是43.为什么x = y + 1t.start()之前执行?我还应该解释在我使用的规则之前发生了什么。

如果我理解了程序顺序规则(一个线程中的每个动作发生 - 在该程序顺序后面的那个线程中的每个动作之前发生)t.start()必须在x = y + 1之前执行,以便该线程t复制变量x,它将是1。

public class HappensBefore {

static int x = 0;
static int y = 42;

public static void main(String[] args) {
    x = 1;
    Thread t = new Thread() {
        public void run() {
            y = x;
            System.out.println(y);
        };
    };
    t.start();
    x = y + 1;
}

5 个答案:

答案 0 :(得分:8)

没有同步,没有volatile字段,没有锁​​定,没有原子字段。代码可以按任何顺序执行。

是的,t.start()将在x = y + 1之前执行。但是启动线程并不意味着线程主体在x = y + 1之前执行。它可以在main()的其余部分之前,之后或之间运行。

答案 1 :(得分:4)

  

如果我理解程序顺序规则(线程中的每个动作)   发生 - 在该线程中的每个动作之前发生的   程序顺序)

不,它没有 在这里你没有一个线程但有两个线程: 主线程和主线程创建并启动的线程:

Thread t = new Thread() {...};
t.start();

JVM的生活线程是设计并发的。

  

如果两个语句已在同一个线程中执行,那么它会   可以安全地假设打印出来的值是" 1"。但如果   两个语句在不同的线程中执行,打印出来的值   可能是" 0",因为不能保证线程A的变化   线程B可以看到计数器 - 除非程序员有   建立了这两者之间发生的关系   语句。

只有当所有陈述都由同一个线程执行或明确创建先发生过的关系时,才会发生之前发生的关系。

摘自Memory Consistency Errors tutorial

  

有几种行为可以创造先发生过的关系。   其中之一是同步,我们将在下面看到   部分。

如上所述,语句由两个线程执行 而且你没有明确地同步陈述 因此,线程之间存在竞争条件,作为一般规则,执行顺序应视为不可预测。 现在在实践中,对于这样一个简短的陈述:x = y + 1

t.start(); 
x = y + 1;

添加操作的分配是如此之短,以至于在t引用的线程被有效运行之前很可能发生。

此外,现代CPU有多个核心 因此,如果CPU具有线程可用性,则不会暂停主线程以使新线程运行。
这两个线程将在"同一时间执行"。
但由于x = y + 1;在启动和运行线程时执行起来要快得多,因此第一个语句只能在第二个语句之前完成。

答案 2 :(得分:3)

根据JMM

  

对线程的start()调用发生在 - 之前的任何操作之前   开始了。

  

如果x和y是同一个线程的动作,x在y之前   程序顺序,然后是hb(x,y)。

程序顺序的定义是:

  

在每个线程t执行的所有线程间动作中,   程序顺序t是反映顺序的总顺序   这些动作将根据内线程执行   语义学。

线程间语义是JMM中明确定义的概念。这意味着每个线程执行的程序中的指令顺序必须在程序文本中写入时保留。

将所有这些应用到您的案例中:

t.start(); hb x = y + 1; //程序订单

t.start(); hb y = x; //在指定规则之前发生here

如果没有额外的同步,我们无法说明x = y + 1;y = x;如何相互关联(从JMM的角度来看)。

如果您正在尝试回答问题"在我的情况下运行时会发生什么?"。可能发生很多事情......看看这个asnwer。运行时可以执行与JMM一致的非平凡优化。

无论如何,如果你对内部感兴趣,你可以看一下article(可以避免记忆障碍)。正如您在生成的程序集中看到的那样,执行volatile读取时不会应用内存屏障。我的意思是运行时无论如何都可以进行优化......只要保留JMM规则......

答案 3 :(得分:2)

t.startx = y + 1之前,它不保证run()方法中的每一行代码都将在x = y + 1之前执行。

事实上,由于竞争条件,打印结果不确定而没有同步,可能是143

答案 4 :(得分:1)

我想补充一点,main方法中的代码在主线程中运行,线程t 不会阻止在您的示例中执行Main。这就是为什么行x = y + 1的执行速度可能比线程 t (如 @davidxxx 已经指出的那样)更快。

如果您在t.join()

之后添加t.start():,则可以观察到不同的行为
...
t.start();
t.join();

在这种情况下,主线程将等待线程 t 完成,输出将为1.