[英]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++
操作都不是原子的。
因此,两种情况都适用:
i++
操作完成一次后,get的操作就完成了。 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.