如果不需要该值,Java 是否允许优化易失性读取,同时删除同步之前发生的情况?
下面的代码示例演示了一种常见方法来演示由缺少的发生前关系导致的并发问题。
private static /*volatile*/ boolean running = true;
public static void main(String[] args) throws InterruptedException {
new Thread() {
@Override
public void run() {
while (running) {
// Do nothing
}
}
}.start();
Thread.sleep(1000);
running = false;
}
如果 是 ,则保证程序在大约一秒后终止。但是,如果不是,则程序根本不能保证终止(因为在这种情况下,不存在发生之前关系或保证变量更改的可见性),这正是我的测试中发生的情况。running
volatile
running
volatile
running
根据 JLS 17.4.5,还可以通过写入和读取另一个变量来强制实施一个发生之前的关系,如下面的代码示例所示。volatile
running2
private static boolean running = true;
private static volatile boolean running2 = true;
public static void main(String[] args) throws InterruptedException {
new Thread() {
@Override
public void run() {
while (running2 || running) {
// Do nothing
}
}
}.start();
Thread.sleep(1000);
running = false;
running2 = false;
}
该变量在每次循环迭代中被读取,当它在大约一秒钟后被读取时,由于发生之前的关系,还可以保证变量随后被读取。因此,该程序保证在大约一秒钟后终止,这正是我的测试中发生的事情。volatile
running2
false
running
false
但是,当我将变量的读取放入循环内的空语句中时(如下面的代码示例所示),程序不会在我的测试中终止。running2
if
while
private static boolean running = true;
private static volatile boolean running2 = true;
public static void main(String[] args) throws InterruptedException {
new Thread() {
@Override
public void run() {
while (running) {
if (running2) {
// Do nothing
}
}
}
}.start();
Thread.sleep(1000);
running = false;
running2 = false;
}
这里的想法是,读取 就像编译器内存屏障:编译器必须使 asm 重新读取非变量,因为读取 可能已与另一个线程中的发布操作同步。这将保证非易失性变量中新值的可见性,例如.volatile
running2
volatile
running2
running
但是我的JVM似乎没有这样做。这是编译器还是JVM错误,或者JLS是否允许这样的优化,即在不需要值时删除易失性
读取?(它只是控制一个空的主体,所以程序行为不依赖于被读取的值,只依赖于创建发生在之前的关系。if
我认为JLS适用于源代码,既然是,由于优化,读取变量的效果不应该被删除。这是编译器还是JVM错误,或者是否有规范,实际上允许进行此类优化?running2
volatile