繁体   English   中英

内存可见性,无需同步或不稳定

[英]memory visibility without synchronized or volatile

说不带“ synchronized”或“ volatile”关键字,一个线程所做的更改将永远不会被另一个线程(或确定性的)看到是正确的吗? 我在多核平台上多次运行以下程序,结果却有所不同。 有时程序永远不会终止,这是预期的场景。 但有时会退出并显示“ 1”。
JDK:jdk1.8.0_73
操作系统:CentOS Linux版本7.1.1503

public class VolatileTest implements Runnable {
    private int i = 0;

    public void run() {
        i++;
        i++;
    }

    public int get() {
        return i;
    }

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        VolatileTest volatileTest = new VolatileTest();
        executorService.execute(volatileTest);
        while (true) {
            int i = volatileTest.get();
            if (i % 2 != 0) { // 
                System.out.format("i: %s \n", i);
                System.exit(0);
            }
        }
    }
}

说不带“ synchronized”或“ volatile”关键字,一个线程所做的更改将永远不会被另一个(或不确定的)线程看到,这是正确的吗?

正确的说法是,如果从写入事件到后续读取事件的关系发生之前没有发生任何事件 ,则未说明会发生什么。

更改可能是立即的,也可能是延迟的,或者可能永远都不可见。

而且,更改可能会以意外的顺序可见。

在您的示例中, i变量可以有3个可能的值,并且main线程将看到哪个变量是不确定的和不可预测的。 您观察到的行为并不意外。

volatileTest.get()的输出可以为0, 1, or 2因为没有同步并且run方法不是原子的。 而且,每个i++操作都不是原子的。

因此,两种情况都适用:

  1. 退出打印1: i++操作完成一次后,get的操作就完成了。
  2. 两条i++指令均已完成,并且get()返回2,从而陷入无限循环。

说不带“ synchronized”或“ volatile”关键字,一个线程所做的更改将永远不会被另一个(或不确定的)线程看到,这是正确的吗?

这是不正确的,这个问题已经回答了( this

运行方法不是原子/线程安全/同步的,因此,很容易理解赋值int i = volatileTest.get(); 可以获取0到2之间的值。要实现所需的功能,您应该获得一个lock并在两次递增后重新实现它,或者只需将关键字synchronized到方法签名即可。

如果没有“ synchronized”或“ volatile”关键字,则一个线程所做的更改将永远不会被另一个线程看到

这是不正确的。 可见性未定义,因为它取决于JRE线程缓存的实现。

executorService.execute(volatileTest);

我认为测试可见度的方法不是最好的。 Jusst搜索stackoverflow并且有多个示例(有些甚至更糟)

该代码执行一次run()方法(并且仅执行一次),根据线程缓存和线程调度程序的不同,您将获得一小段更改, i==1

暂无
暂无

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

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