有如下代码:
package logTest;
import lombok.extern.slf4j.Slf4j;
/**
* @Author:wjy
*/
@Slf4j(topic = "c.test")
public class Test9_volitile {
static boolean run = true;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while (run) {
}
});
t1.start();
Thread.sleep(1000);
log.debug("停止 t");
run = false;
}
}
可以看到,代码运行后会进入死循环
这是因为当 t1 线程刚从主内存读取了run的值到工作内存后,因为 t1 线程要频繁从主内存中读取 run 的值,JIT 编译器会将 run 的值缓存至自己工作内存中的高速缓存中, 减少对主存中 run 的访问,提高效率
在1
秒之后,
main
线程修改了
run
的值,并同步至主存,而
t1
是从自己工作内存中的高速缓存中读取这个变量的值,结果永远是旧值
当然,我们知道,只需要将run变量加上一个 volatile 就可以解决这个问题,然而,当我们在while循环中加入输出语句时,也可以跳出死循环,如图:
package logTest;
import lombok.extern.slf4j.Slf4j;
/**
* @Author:wjy
*/
@Slf4j(topic = "c.test")
public class Test9_volitile {
static boolean run = true;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while (run) {
System.out.println(1);
}
});
t1.start();
Thread.sleep(1000);
log.debug("停止 t");
run = false;
}
}
查看println的源码,可以看到:
println内部加了 synchronized 锁 也就是可重入锁加锁
在加锁时,将清空工作内存中的共享变量的值,从而使用共享变量时需要从主内存中重新读取最
新的值,因此,主线程中的 run值就会被读取,从而跳出循环