美文网首页
Android volatile 原理。

Android volatile 原理。

作者: ZSGZ_AD | 来源:发表于2020-12-04 18:31 被阅读0次

在《Java 并发编程:核心理论》一文中,我们已经提到可见性、有序性及原子性 问题,通常情况下我们可以通过 Synchronized 关键字来解决这些个问题,不过 如果对 Synchonized 原理有了解的话,应该知道 Synchronized 是一个较重量级 的操作,对系统的性能有比较大的影响,所以如果有其他解决方案,我们通常都 避免使用 Synchronized 来解决问题。

而 volatile 关键字就是 Java 中提供的另一种解决可见性有序性问题的方案。对于 原子性,需要强调一点,也是大家容易误解的一点:对 volatile 变量的单次读/ 写操作可保证原子性的,如 long 和 double 类型变量,但是并不能保证 i++这种 操作的原子性,因为本质上 i++是读、写两次操作。 volatile 也是互斥同步的一种实现,不过它非常的轻量级。 volatile 的意义? 线程会一直等待。 可以尝试获得锁,线程可以不用一直等待 锁状态 无法判断 可以判断 锁类型 可重入 不可中断 非公平 可重入 可判断 可公平(两者皆可) 性能 少量同步 大量同步

  • 防止 CPU 指令重排序 volatile 有两条关键的语义: 保证被 volatile 修饰的变量对所有线程都是可见的 禁止进行指令重排序 要理解 volatile 关键字,我们得先从 Java 的线程模型开始说起。如图所示:


    image.png

    Java 内存模型规定了所有字段(这些字段包括实例字段、静态字段等,不包括局 部变量、方法参数等,因为这些是线程私有的,并不存在竞争)都存在主内存中, 每个线程会 有自己的工作内存,工作内存里保存了线程所使用到的变量在主内 存里的副本拷贝,线程对变量的操作只能在工作内存里进行,而不能直接读写主 内存,当然不同内存之间也 无法直接访问对方的工作内存,也就是说主内存是 线程传值的媒介。
    我们来理解第一句话:
    保证被 volatile 修饰的变量对所有线程都是可见的 如何保证可见性?
    被 volatile 修饰的变量在工作内存修改后会被强制写回主内存,其他线程在使用 时也会强制从主内存刷新,这样就保证了一致性。 关于“保证被 volatile 修饰的变量对所有线程都是可见的”,有种常见的错误理解:

  • 由于 volatile 修饰的变量在各个线程里都是一致的,所以基于 volatile 变 量的运算在多线程并发的情况下是安全的。 这句话的前半部分是对的,后半部分却错了,因此它忘记考虑变量的操作是否具有原子性这一问题。
    举个例子:
    private volatile int start = 0;
    private void volatile Keyword() {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    start++;
                }
            }
        }; for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(runnable); thread.start();
        } Log.d(TAG, "start = " + start);
    }
image.png

这段代码启动了 10 个线程,每次 10 次自增,按道理最终结果应该是 100,但是 结果并非如此。 为什么会这样?
仔细看一下 start++,它其实并非一个原子操作,简单来看,它有两步:
1、取出 start 的值,因为有 volatile 的修饰,这时候的值是正确的。
2、自增,但是自增的时候,别的线程可能已经把 start 加大了,这种情况下就有 可能把较小的 start 写回主内存中。
所以 volatile 只能保证可见性,在不符合以 下场景下我们依然需要通过加锁来保证原子性:

  • 运算结果并不依赖变量当前的值,或者只有单一线程修改变量的值。(要 么结果不依赖当前值,要么操作是原子性的,要么只要一个线程修改变量 的值) - 变量不需要与其他状态变量共同参与不变约束 比方说我们会在线程里加 个 boolean 变量,来判断线程是否停止,这种情况就非常适合使用 volatile。
    我们再来理解第二句话。 禁止进行指令重排序 什么是指令重排序?
    指令重排序是指指令乱序执行,即在条件允许的情况下直接运行当前有能 力立即执行的后续指令,避开为获取一条指令所需数据而造成的等待,通 过乱序执行的技术提供执行效率。 指令重排序会在被 volatile 修饰的变量的赋值操作前,添加一个内存屏障, 指令重排序时不能把后面的指令重排序移到内存屏障之前的位置。

相关文章

  • Android volatile 原理。

    在《Java 并发编程:核心理论》一文中,我们已经提到可见性、有序性及原子性 问题,通常情况下我们可以通过 Syn...

  • 互联网JAVA面试常问问题(三)

    一、volatile原理和使用场景 volatile 原理 volatile变量进行写操作时,JVM会向处理器发送...

  • volatile学习

    目录:1.volatile是什么2.volatile的作用3.volatile的原理4.volatile与sync...

  • Java并发机制底层实现原理-volatile

    章节目录 volatile的实现原理与应用 1.volatile的实现原理与应用 Java source code...

  • 并发编程艺术-2

    本篇文章主要介绍并发的底层实现原理:volatile , synchronized, 原子操作。 volatile...

  • volatile与synchronized的区别

    一、volatile volatile的原理在java中,被volatile声明的关键字,jvm会在翻译的时候在c...

  • java并发(二):深入分析volatile实现原理

    volatile的原理实现可以看这篇文章,真的是从硬件层面上说明了volatile怎样保证可见性。volatile...

  • JAVA线程安全之volatile

    volatile volatile原理是基于CPU内存屏障(Memory Barrier)指令实现的; 如果一个变...

  • volatile原理

    计算机内存模型 计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据的读取和写入。由...

  • volatile原理

    通过前面一章我们了解了synchronized是一个重量级的锁,虽然JVM对它做了很多优化,而下面介绍的volat...

网友评论

      本文标题:Android volatile 原理。

      本文链接:https://www.haomeiwen.com/subject/wxlliktx.html