当前位置: 移动技术网 > IT编程>开发语言>Java > 我就站在你面前,你却视而不见!

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

2020年04月15日  | 移动技术网IT编程  | 我要评论

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

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

可见性

首先看下可见性的概念:

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

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

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

可见性问题

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

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

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

单核cpu

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

然而在多核 cpu 上,由于每个 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并发编程实战

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网