线程难以理解的行为

问题描述 投票:1回答:3

我正在阅读J. Bloch的Effective Java,在并发章节中有一个例子:

public class Main {

    private static boolean stop;

    public static void main(String[] args) throws InterruptedException {

        new Thread(() -> {
            int i = 0;
            while (!stop) {
                i++;
            }
        }).start();

        TimeUnit.SECONDS.sleep(1);
        stop = true;
    }
}

此示例显示没有同步子线程将不会停止,因为它无法看到主线程所做的更改。但是,如果我们将代码更改为:

public class Main {

    private static boolean stop;

    public static void main(String[] args) throws InterruptedException {

        new Thread(() -> {
            while (!stop) {
                System.out.println(i);
            }
        }).start();

        TimeUnit.SECONDS.sleep(1);
        stop = true;
    }
}

申请将在1秒后停止。那么有人可以解释为什么System.out.println同步线程不像第一个变体那样吗?

java multithreading
3个回答
3
投票

该书解释说,这是因为提升编译器优化,第一个代码可能永远运行。

简而言之,代码:

while (!stop)
    i++;

可能会改为:

if (!stop)
    while (true)
        i++;

这实际上意味着后台(第二个)线程在第一个例子中继续运行;但优化不适用于第二个代码。仅提出同步作为避免编译器优化的方法之一。有关此优化的更多信息,请访问this questionthis one


2
投票

实际上,正确的解决方案是将变量stop声明为volatile。


1
投票

仅仅因为你的程序在测试时停止并不意味着它会保证停止。您的代码已损坏,但您可能会使用它,部分原因是某些CPU架构(包括x86)具有比Java更强大的内存模型。在Java支持的其他体系结构上,您的程序可能无法停止。这就是线程安全漏洞如此阴险的原因。测试线程安全性非常困难,因此您只需了解规则并遵循它们即可。

© www.soinside.com 2019 - 2024. All rights reserved.