前言
该问题在看JUC课程的时候遇到,记录一下
背景知识
- 该问题主要与可见性有关
- 从JMM我们可以知道,线程先将变量从主内存拷贝到工作内存,更新完成后再刷新回主内存。
- 此时如果有多个线程并发修改共享变量,就会出现,A、B同时拷贝变量到工作内存,A修改后,B是感受不到的
举个例子
@Slf4j(topic = "c.VolatileDemo")
public class VolatileDemo {
static int cache = 0;
public static void main(String[] args) {
new Thread(() -> {
try {TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) {e.printStackTrace(); }
cache = 1;
log.debug("cache=1");
}, "t1").start();
new Thread(() -> {
while(cache!=1){
}
log.debug("捕捉到cache改变");
}, "t2").start();
}
}
- 上述例子中,t1线程对cache进行了修改,t2是感知不到的
- 结果就是t2线程一直while循环,不会捕捉到改变
- 这是一个描述可见性很好的例子
那么解决办法是给cache变量添加volatile关键字
问题
问题是在实践的过程中发现,线程在sleep后,会重新从主内存获取共享变量。即代码如下,不加volatile也能捕捉改变
@Slf4j(topic = "c.VolatileDemo")
public class VolatileDemo {
static int cache = 0;
public static void main(String[] args) {
new Thread(() -> {
try {TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) {e.printStackTrace(); }
cache = 1;
log.debug("cache=1");
}, "t1").start();
new Thread(() -> {
while(cache!=1){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug("捕捉到cache改变");
}, "t2").start();
}
}
15:03:00.024 c.VolatileDemo [t1] - cache=1
15:03:00.024 c.VolatileDemo [t2] - 捕捉到cache改变
据此猜测sleep后回重刷内存
后来查阅了一些资料得出的结论是
sleep前不一定会将工作内存更新到主内存,sleep后也不一定会重新load,只有volatile才是最可靠的