java中非volatile变量读取是从工作内存中读取,不一定可以读到主内存的最新数据。volatile变量保证每次读取都要从主内存中拿。
写了一段代码来验证这一点
public class MemoryMode {
private volatile boolean flag = false;//这个类型是volatile的时候,结果是conditionChanged:true
public void setFlag(){
flag = true;
}
private boolean conditionChanged = false;
public static class LoopRunnable implements Runnable{
private MemoryMode memoryMode;
public LoopRunnable(MemoryMode memoryMode){
this.memoryMode = memoryMode;
}
public void run() {
while (true){//标记未改变,一直循环
if(memoryMode.flag){
memoryMode.conditionChanged = true;
break;
}
}
}
}
public static class SetFlagRunnable implements Runnable{
private MemoryMode memoryMode;
public SetFlagRunnable(MemoryMode memoryMode){
this.memoryMode = memoryMode;
}
public void run() {
memoryMode.setFlag();//改变标记值
}
}
public static void main(String[] args) throws InterruptedException {
MemoryMode memoryMode = new MemoryMode();
LoopRunnable loopRunnable = new LoopRunnable(memoryMode);
Thread a = new Thread(loopRunnable);
Thread b = new Thread(new SetFlagRunnable(memoryMode));
a.start();
Thread.sleep(10000);
b.start();
a.join();
b.join();
System.out.print("conditionChanged:"+memoryMode.conditionChanged);
}
}
上面这段代码申明了一个volatile修饰的名称为flag的变量,初始值为false。启动一个线程a,run方法是一个死循环,flag为true时才退出循环。然后启动另外一个线程b,改变flag的值到true。因为flag一个volatile变量,值被线程b改变后,对线程a是立即可见的,所以可以代码可以正常退出。
把上面的代码修改一下,去掉flag对象的volatile修饰。期待a线程一直读不到flag的值变为true,代码一直循环,不能正常退出。
在Intellij里,选择run as application,和期望的一样,代码确实不能正常退出。但是,如果选择的是debug as application的话,代码可以正常退出的,和标记为volatile的效果一样。
可以猜想,debug模式下,所有变量都是从主内存中读取的。