美文网首页
神秘的volatile关键字

神秘的volatile关键字

作者: 邓立_全栈UncleLi | 来源:发表于2022-04-13 09:54 被阅读0次

    前言
    volatile关键字是面试中常问的知识点,包括三点:可见性、有序性、非原子性。接下来就说一下这三点。

    JMM(Java Memory Model - Java内存模型)

    • 每个 Java 线程都有⾃⼰的⼯作内存。操作数据,⾸先从主内存中读,得到⼀份拷⻉,操作完毕后再写回主内存

    • JMM可能带来可⻅性、原⼦性和有序性问题

      • 可⻅性:是指某个线程对主内存内容的修改,应该⽴刻通知其它线程
      • 有序性:是指指令是有序的,不会被重排
      • 原⼦性:是指⼀个操作是不可分割的,完整性,也即某个线程正在做某个具体业务时,中间不可以被加塞或者被分割。需要
        整体完整,要么同时成功,要么同时失败,不能执⾏⼀半就不执⾏

    volatile 关键字

    • 它是 Java 提供的⼀种轻量级同步机制,能保证可⻅性和有序性,但是不能保证原⼦性

    volatile 可见性

    可见性图一
    • 发现在 38 行代码处更新了数据值,但 45 行的 while 循环还是一直在运行,无法执行 49 行代码的打印。程序真的一直在 while 的循环里一直执行吗?那就在循环里加个打印吧,如下图
    可见性图二
    • 此时发现程序竟然停止了,没有一直在循环里,而且值也更新成功了。那是为什么呢?其实是因为打印语句 System.out.println 里有 synchronized 关键字修饰,也是触发可见性方法之一,如下图
    可见性图三
    • 那么不加打印如何解决这个问题呢?答案就是给类变量 num 加上 volatile 关键字。如下图
    可见性图四

    volatile 有序性

    • 计算机在执⾏程序时,为了提⾼性能,编译器和处理器常常会对指令做重排,⼀般分以下三种

      • 单线程环境⾥⾯确保程序最终执⾏结果和代码顺序执⾏的结果⼀致
      • 处理器在进⾏重排序时必须要考虑指令之间的数据依赖性
      • 多线程环境中线程交替执⾏,由于编译器优化重排的存在,两个线程中使⽤的变量能否保证⼀致性是⽆法确定的,结果⽆法预测
    • 如图方法 test 中的 a、b 变量毫无关联,则编译器和处理器可能会先执行 b 变量的赋值,而不是一定按顺序从上往下执行

    有序性

    volatile 非原子性

    非原子性图一
    • 就算加上了 volatile 关键字,也无法保证结果就是预想中的值。这是因为 num++ 操作会形成 4 条指令。如下图
    不能保证原子性图二
    • 要想保证数据正确性,可使用 JUC 提供的 int 原子封装类 AtomicInteger 来进行自增,如下图
    原子性

    volatile 使用场景

    • 单例模式
    单例模式
    • new SingletonDemo 可能会发生指令重排,所以加上 volatile 关键字修饰 instance 实例。如下图
    指令重排

    结语
    这就是 volatile 关键字面试中常问的知识点,希望对您有帮助。

    相关文章

      网友评论

          本文标题:神秘的volatile关键字

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