记录一次可见性问题引发的思考

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隧道检测平民解决方案

REDIS09_分布式锁的概述加锁使用sexnu解锁使用lua脚本保证原子性引发的问题思考

一次405问题引发的思考