繁体   English   中英

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

[英]Happens-before rules in Java Memory Model

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

如果我理解了程序顺序规则(一个线程中的每个动作发生在该程序顺序后面的那个线程中的每个动作之前t.start()则必须在x = y + 1之前执行t.start() ,以便线程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;
}

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

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

根据JMM的说法:

在启动线程中的任何操作之前,对线程的start()调用发生。

如果x和y是同一个线程的动作,并且x在程序顺序中出现在y之前,那么hb(x,y)。

程序顺序的定义是这样的:

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

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

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

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

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

没有额外的同步,我们不能说x = y + 1; y = x; 相互关联(从JMM的角度来看)。

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

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

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

不,不。
这里你没有一个线程而是两个线程:主线程和主线程创建并启动的线程:

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

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

如果两个语句已在同一个线程中执行,则可以安全地假设打印出的值为“1”。 但是如果这两个语句是在不同的线程中执行的,那么打印出的值可能是“0”,因为不能保证线程A对计数器的更改对于线程B是可见的 - 除非程序员已经建立了之前发生的关系。这两个陈述。

只有当所有语句都被同一个线程执行,或者如果明确创建的之前发生关系发生之前发生关系。

内存一致性错误教程中提取:

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

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

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

添加操作的分配很短,无法执行,因为在t引用的线程有效运行之前很可能发生。

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

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

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

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

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

...
t.start();
t.join();

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM