记录一次可见性问题引发的思考
Posted 结构化思维wz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了记录一次可见性问题引发的思考相关的知识,希望对你有一定的参考价值。
可见性问题
欢迎大家在评论区说出自己的想法
案例描述:
- 写一个while循环,条件变量设置为ture,创建一个线程(
线程0
)调执行此方法。 - 用main线程修改条件变量,看是否while循环结束了。
- 如果结束了,证明main线程在工作内存中修改的条件变量被
线程0
读取到了。 - 如果没结束,证明main线程在工作内存中修改的条件变量没有被
线程0
读取到。
public class TestVolatile
public static void main(String[] args)
PrintString printString = new PrintString();
//新建线程调用打印字符串的方法
new Thread(printString::printStringMethod).start();
//main线程睡眠1000毫秒
try
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"线程sleep 1000.....");
catch (InterruptedException e)
e.printStackTrace();
System.out.println("在main线程中修改打印标志");
printString.setContinuePrint(false);
//在main修改玩打印标志后,子线程是否结束打印。
static class PrintString
private boolean continuePrint = true;
public void printStringMethod()
System.out.println("打印开始");
while (continuePrint)
System.out.println("打印结束");
public void setContinuePrint(boolean continuePrint)
this.continuePrint = continuePrint;
这种写法的运行结果符合预期:
如果加上volatile
关键字:
也符合预期。
问题出现:
但是,如果在while循环里加入其他操作后
static class PrintString
private boolean continuePrint = true;
public void printStringMethod()
System.out.println("打印开始");
while (continuePrint)
//500ms打印一次
System.out.println(Thread.currentThread().getName()+"打印中");
try
Thread.sleep(500);
catch (InterruptedException e)
e.printStackTrace();
System.out.println("打印结束");
public void setContinuePrint(boolean continuePrint)
this.continuePrint = continuePrint;
在不使用volatile
关键字的前提下,程序也结束了
不加volatile
关键字的情况下程序依旧结束了原因在于CPU的缓存一致性协议:
CPU的缓存一致性协议
例如 Intel 的MESI协议,MESI协议保证了每个缓存中使用的共享变量的副本是一致的,它核心的思想是:当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,发现自己缓存中缓存该变量的缓存行是无效的,那么它就会从内存重新读取。
在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,发现自己缓存中缓存该变量的缓存行是无效的,那么它就会从内存重新读取。
欢迎大家在评论区说出自己的想法!!
以上是关于记录一次可见性问题引发的思考的主要内容,如果未能解决你的问题,请参考以下文章
一次误报引发的DNS检测方案的思考:DNS隧道检测平民解决方案