没有synchronized或volatile的内存可见性

时间:2018-01-14 07:50:36

标签: java multithreading

说没有“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);
            }
        }
    }
}

4 个答案:

答案 0 :(得分:4)

  

说没有“synchronized”或“volatile”关键字是否正确,一个线程所做的更改永远不会被另一个(或非确定性的)看到?

正确的说法是,如果在从写事件到后续读事件的关系之前没有发生,则未指定会发生什么。

可能会立即或延迟更改,或者可能永远不会显示更改。

而且,更改可能会以意想不到的顺序显现。

在您的示例中,i变量可以有3个可能的值,并且main线程将看到它未指定且不可预测。您观察到的行为并非意料之外。

答案 1 :(得分:1)

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

因此两种情况都适用:

  1. 退出打印1:i++操作完成后,获得的结果为1。
  2. 两条i++指令均已完成,get()返回2,因此陷入了无限循环。

答案 2 :(得分:0)

  

没有“synchronized”或“volatile”关键字说是不正确的,   一个线程所做的更改永远不会被另一个线程看到(或者   非确定性)?

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

run方法不是原子/线程安全/同步的,所以很容易理解,分配int i = volatileTest.get();可以从0到2获得值。为了实现你想要的,你应该获得{{1并在双增量之后重新发布它,或者只是将关键字lock放到方法签名中。

答案 3 :(得分:0)

  

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

看到

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

  

executorService.execute(volatileTest);

我相信你测试能见度的方式不是最好的。 Jusst搜索stackoverflow并且有多个例子(有些甚至更糟)

代码执行run()方法一次(并且只执行一次),根据线程缓存和线程调度程序,您将得到一个小的更改synchronized