说没有“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);
}
}
}
}
答案 0 :(得分:4)
说没有“synchronized”或“volatile”关键字是否正确,一个线程所做的更改永远不会被另一个(或非确定性的)看到?
正确的说法是,如果在从写事件到后续读事件的关系之前没有发生,则未指定会发生什么。
可能会立即或延迟更改,或者可能永远不会显示更改。
而且,更改可能会以意想不到的顺序显现。
在您的示例中,i
变量可以有3个可能的值,并且main
线程将看到它未指定且不可预测。您观察到的行为并非意料之外。
答案 1 :(得分:1)
volatileTest.get()
的输出可以是0, 1, or 2
,因为没有同步且run方法不是原子的。而且,每个i++
操作都不是原子操作。
因此两种情况都适用:
i++
操作完成后,获得的结果为1。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