美文网首页
神秘的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