专栏首页武培轩的专栏我就站在你面前,你却视而不见!

我就站在你面前,你却视而不见!

在上一篇文章一男子给对象转账5000元,居然又退还了!中,我们学习了并发三大特性之一的原子性,并对原子性问题进行分析。

这篇文章我们就一起来了解下可见性:

可见性

首先看下可见性的概念:

可见性就是指某一个线程修改了共享变量的值时,其他线程能够立即得知这个修改

什么?难道变量被修改了,线程不应该马上读取到的吗?为什么和我认知的不一样呢?

好的,那么接下来让我们带着问题,一起来搞懂可见性问题。

可见性问题

可见性问题的元凶就是 CPU 缓存,都怪 CPU 为程序性能优化做的努力,搞出这么多幺蛾子。

关于 CPU 缓存可以阅读:原来 CPU 为程序性能优化做了这么多

首先在单核 CPU 上,是不存在可见性问题的,因为所有的线程都在一个 CPU 上执行,所有的线程都是操作同一 CPU 缓存,某一个线程修改了共享变量的值,另外的线程也可以马上读取到,因此是可见的。

如上图所示,Thread-0Thread-1 都是在一个 CPU 缓存上进行操作,所以 Thread-0 修改了变量 flag 的值后,Thread-1 再去访问变量 flag,得到的一定是最新的 flag 值。

然而在多核 CPU 上,由于每个 CPU 都有自己的缓存,当多个不同线程运行在不同的 CPU 上时,这些线程操作的 CPU 缓存也是不同的,因此某一个线程对共享变量进行修改时,另外的线程读取到的不一定是最新值,也就不具有可见性了。

如上图所示,Thread-0 是在 CPU-0 上的缓存进行操作,Thread-1 是在 CPU-1 上的缓存进行操作,所以 Thread-0 修改了变量 flag 的值后,Thread-1 再去访问变量 flag,得到的不一定是最新的 flag 值,因此 Thread-0 对共享变量 flag 的修改对 Thread-1 是不可见的。

下面用一个例子来看下可见性问题,创建一个 VisibilityTest 类,实现 Runnable 接口,在 run() 方法中判断 flag 是否为 true,若为 true 则进行打印操作,主方法中启动一个线程 thread,主线程等待 0.5 秒后,将 flag 的值设为 true

public class VisibilityDemo {
    private static class VisibilityTest implements Runnable {
        private boolean flag = false;
        @Override
        public void run() {
            while (true) {
                if (flag) {
                    System.out.println(Thread.currentThread().getName() + ":" + flag);
                }
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        VisibilityTest visibilityTest = new VisibilityTest();
        Thread thread = new Thread(visibilityTest);
        thread.start();
        // 等待线程启动
        Thread.sleep(500);
        // 更新 flag 为 true
        visibilityTest.flag = true;
        System.out.println(Thread.currentThread().getName() + ":" + visibilityTest.flag);
    }
}

发现输出的结果为:main:true,和我们想象的不太一样,按道理 thread 应该会持续打印出 Thread-0:true 的,但是并非如此,这也就验证了我们刚才讲的可见性问题。

那么如何解决可见性问题呢?

可以采用同步的方式去解决或者使用 volatile 关键字也可以保证可见性。

关于 volatile 相关原理可以阅读:你真的了解 volatile 关键字吗?

总结

本文学习了线程安全三大特性之中的可见性,另外 CPU 缓存在提高程序性能的同时也带来了可见性问题,只有我们理解了可见性的原理,才更容易去诊断并发编程中的 BUG。

参考 《Java并发编程实战》 《深入理解Java虚拟机:JVM高级特性与最佳实践》 《实战Java高并发程序设计》 《Java多线程编程核心技术》 Java并发编程实战

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 可视化对象排序之——你看不见我,可我却一直在左右你

    几乎所有的可视化对象都是需要排序的:按照可视化对象数值大小,按照字母顺序排列,等等。

    陈学谦
  • 关于信息无障碍我们能做些什么

    严格意义上来说,我是一名前端开发,清楚地记得我参与过的项目曾花费大量的人力和时间对网站的UI进行重新改版设计,尽可能多地吸引用户,也曾花费大量人力和时间优化网站...

    ThoughtWorks
  • 你知道了吗?2015年网页设计的9大趋势

    其实在此之前已经有很多小伙伴翻译或总结过类似文章来分析优秀网页设计的趋势,不过我还是希望我总结的东西能更宏观一点并为大家带来一些新的东西。 稍后看官们可能会觉得...

    wangxl
  • SEO工作,如何避免内忧外患的状态?

    内忧外患这个词我们都知道,通常一个国家如果出现了内忧外患,那恐怕对国家的发展是不利的,我们做SEO时,一个网站就好比一个国家,在整个搜索引擎的世界中生存着,为了...

    蝙蝠侠IT
  • 我试了下《复仇者联盟》AI换脸系列,当了英雄的我现在很慌...

    《复仇者联盟4:终局之战》上映已经有一段时间了,内地累计票房便已突破20亿。电影精彩之处离不开钢铁侠、雷神、美国队长等各位超级英雄的实力支撑。

    小小詹同学
  • 我试了下《复仇者联盟》AI换脸系列,当了英雄的我现在很慌...

    《复仇者联盟4:终局之战》上映已经有一段时间了,内地累计票房便已突破20亿。电影精彩之处离不开钢铁侠、雷神、美国队长等各位超级英雄的实力支撑。

    统计学家
  • 我试了下《复仇者联盟》AI换脸系列,当了英雄的我现在很慌...

    《复仇者联盟4:终局之战》上映已经有一段时间了,内地累计票房便已突破20亿。电影精彩之处离不开钢铁侠、雷神、美国队长等各位超级英雄的实力支撑。

    大数据文摘
  • 键盘敲击识别技术真的靠谱吗?

    所有人都知道密码是靠不住的。于是现在有一个有意思的行为生物识别是“你是如何打字的”,或称为输入行为生物识别技术。 生物识别正在广泛推广 大多数网络用户在选择密码...

    FB客服
  • 看设计师大牛如何将用户体验与建站融会贯通

    在建站中应用各种用户体验设计技巧就如绘画和雕塑一般,都需要耐心、远见、审美、创造力和对细节的体察。你一手建立的网站很大程度上代表着你的公司、你的产品,所以它必...

    奔跑的小鹿

扫码关注云+社区

领取腾讯云代金券