Volatile 解决线程可见性的问题
有如下一段程序代码:
public class Demo {
private static boolean stop = false;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
int i=0;
while(!stop){
i++;
}
System.out.println("the thread is stopped.i="+i);
});
thread.start();
Thread.sleep(1000);
stop = true;
}
}
执行后,没有任何输出。线程却不停止。 说明while循环仍在执行,那么stop的值仍为false,说明stop=true 对while并没有生效。 这就是多线程的可见性问题。 就是我们在外层将值改变,子线程没有看到变化。 如果要解决它,可以使用volatile关键字。
public class Demo {
private volatile static boolean stop = false;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
int i=0;
while(!stop){
i++;
}
System.out.println("the thread is stopped.i="+i);
});
thread.start();
Thread.sleep(1000);
stop = true;
}
}
这次的结果为: 除了使用volatile关键字外, 如果在循环体while 中加入 Thread.sleep(0); 或者 System.out.println("the i="+i); 也可以停止线程。
为什么 Thread.sleep可以停止线程? 加Thread.sleep(0) 可以停止线程的原因是: 这是因为在hotspot的server端虚拟机中,做了深度优化(client版本就没有这个问题)。
C:Users86137>java -version java version "1.8.0_361" Java(TM) SE Runtime Environment (build 1.8.0_361-b09) Java HotSpot(TM) 64-Bit Server VM (build 25.361-b09, mixed mode)
JIT 做了如下的优化:
while(!stop){
i++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//-->
if(!stop){
while(true){
i++;
}
}
volatile可以禁止JIT的优化过程。
除了volatile,我们可以通过VM参数来设置JIT禁止优化: 首先我们把代码中private tatic boolean stop = false; 中的volatile去掉; 然后在执行的VM参数中配置-Djava.compiler=NONE 就会发现 while中的stop生效了,线程会停下来。
为什么print可以导致循环结束? print中包括两个步骤,1.IO操作;2.Synchronized。 Synchronized的释放,会将工作内存中的操作同步到主内存。所以stop最新的值会被while(!stop)读取到。 我们可以从如下示例中看到这个现象:
while(!stop){
i++;
synchronized (Demo.class){
}
}
加了一个Synchronized的锁,会发现线程开启执行后,会停下来。
而IO操作会涉及到与内存、硬盘的交互,它会阻塞线程。 比如:
while(!stop){
i++;
new File("test.java");
}
加了IO操作,也会使线程停下来。
