volatile 是并发编程中的重要关键字,它的名气甚至是可以与 synchronized、ReentrantLock 等齐名,也是属于并发编程五杰之一。

需要注意的是 volatile 并不能保证原子性,因此使用 volatile 并没有办法保证线程安全

并发编程五杰:

PS:“并发编程五杰”是我个人起的名字,大家也不用太当真。

1.什么是 volatile?

volatile 是 Java 中的一个关键字,用于修饰变量,它的主要作用是保证变量的可见性和禁止指令重排序

  • 可见性:是指当一个线程修改了一个被 volatile 修饰的变量时,其他线程能够立即看到这个修改。
  • 禁止指令重排序:则是确保对 volatile 变量的读写操作不会被编译器或处理器随意重新排序,从而保证了程序执行的顺序符合我们的预期。

2.volatile 工作原理

为了实现可见性,Java 内存模型(JMM)会在对 volatile 变量进行写操作时,强制将工作内存中的值刷新到主内存,并在读取时强制从主内存中重新获取最新的值。

而禁止指令重排序是通过在编译器和处理器层面添加特定的内存屏障指令来实现的。

具体来说。

2.1 可见性实现原理

可见性:在计算机编程特别是多线程编程中,“可见性”指的是一个线程对共享变量的修改,对于其他线程是否能够及时地、准确地“可见”,即其他线程是否能够及时感知到这个修改并获取到最新的值。

例如,在一个多线程环境中,如果线程 A 修改了一个共享变量的值,而线程 B 无法立即看到这个修改,那么就存在可见性问题。

多线程操作共享变量流程如下:



volatile 是通过内存屏障(Memory Barrier) 来确保可见性。

  • 写屏障(Store Barrier):在 volatile 变量的写操作之后插入写屏障,确保所有之前的写操作都同步到主内存中,从而使得其他线程在读取该变量时能够获取到最新的值。
  • 读屏障(Load Barrier):在 volatile 变量的读操作之前插入读屏障,确保所有之前的写操作都已完成,从而读取到的是最新的值。

通过这种方式,volatile 变量在多线程环境下的读写操作能够保持较高的可见性,但需要注意的是,volatile 并不保证操作的原子性。

具体来说,volatile 内存可见性主要通过 lock 前缀指令实现的,它会锁定当前内存区域的缓存(缓存行),并且立即将当前缓存行数据写入主内存(耗时非常短),回写主内存的时候会通知其他线程缓存了该变量的地址失效,从而导致其他线程需要重新去主内存中重新读取数据到其工作线程中。

2.2 有序性实现原理

volatile 的有序性是通过插入内存屏障,在内存屏障前后禁止重排序优化,以此实现有序性的。

2.3 正确理解“内存屏障”?

volatile 保证可见性的“内存屏障”和保证有序性的“内存屏障”有什么区别呢?

在说它们的区别之前,我们现需要对“内存屏障”有一个大致的理解。

内存屏障,简单来说,就像是在内存操作中的一道“关卡”或者“栅栏”。

想象一下,计算机在执行程序的时候,为了提高效率,可能会对指令的执行顺序进行一些调整。但是在多线程或者多核心的环境下,这种随意的调整可能会导致一些问题。

内存屏障的作用就是阻止这种随意的调整,确保特定的内存操作按照我们期望的顺序执行。

所以“内存屏障”本身只是一种“技术”,而这种“技术”可以实现很多“业务功能”。

这就像 Spring 中的 AOP 一样,AOP 是一种“技术”,而这种技术可以实现很多业务功能。例如,针对日志处理可以使用 AOP、针对用户鉴权可以使用 AOP 等,而内存屏障也是一样,我们可以使用内存屏障实现可见性的“业务功能”,也可以实现有序性的“业务功能”等。

3.volatile 适用场景

volatile 常见场景有以下两种:

  1. 状态标记
  2. 单例模式中的双重检查锁

具体来说。

3.1 状态标记

例如,在多线程环境中用于表示某个任务是否完成的标志变量,具体代码如下:

volatile boolean isTaskFinished = false;

3.2 单例模式中的双重检查锁

class Singleton {
private volatile static Singleton instance; public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}

4.volatile 局限性

volatile 并不能保证原子性,也就是并不能保证线程安全

例如,对于 i++ 这样的操作,它不是一个原子操作,单纯使用 volatile 修饰 i 并不能保证线程安全。

课后思考

为什么双重效验锁一定要加 volatile?不是已经加锁了吗?

本文已收录到我的面试小站 www.javacn.site,其中包含的内容有:Redis、JVM、并发、并发、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、设计模式、消息队列等模块。

面试官:说说volatile应用和实现原理?的更多相关文章

  1. 面试官:volatile关键字用过吧?说一下作用和实现吧

    volatile    可见性的本质类似于CPU的缓存一致性问题,线程内部的副本类似于告诉缓存区 面试官:volatile关键字用过吧?说一下作用和实现吧 https://blog.csdn.net/ ...

  2. 面试官再问你 HashMap 底层原理,就把这篇文章甩给他看

    前言 HashMap 源码和底层原理在现在面试中是必问的.因此,我们非常有必要搞清楚它的底层实现和思想,才能在面试中对答如流,跟面试官大战三百回合.文章较长,介绍了很多原理性的问题,希望对你有所帮助~ ...

  3. 面试官:SpringBoot jar 可执行原理,知道吗?

    文章篇幅较长,但是包含了SpringBoot 可执行jar包从头到尾的原理,请读者耐心观看.同时文章是基于 SpringBoot-2.1.3进行分析.涉及的知识点主要包括Maven的生命周期以及自定义 ...

  4. 面试官最爱的volatile关键字

    在Java相关的岗位面试中,很多面试官都喜欢考察面试者对Java并发的了解程度,而以volatile关键字作为一个小的切入点,往往可以一问到底,把Java内存模型(JMM),Java并发编程的一些特性 ...

  5. Java面试官最爱问的volatile关键字

    在Java的面试当中,面试官最爱问的就是volatile关键字相关的问题.经过多次面试之后,你是否思考过,为什么他们那么爱问volatile关键字相关的问题?而对于你,如果作为面试官,是否也会考虑采用 ...

  6. 面试官:说一下Synchronized底层实现,锁升级的具体过程?

    面试官:说一下Synchronized底层实现,锁升级的具体过程? 这是我去年7,8月份面试的时候被问的一个面试题,说实话被问到这个问题还是很意外的,感觉这个东西没啥用啊,直到后面被问了一波new O ...

  7. 面试中的volatile关键字

    在Java的面试当中,面试官最爱问的就是volatile关键字相关的内容.经过多次面试之后,你是否思考过,为什么他们那么爱问volatile关键字相关的问题?而对于你,如果作为面试官,是否也会考虑采用 ...

  8. 反制面试官 | 14张原理图 | 再也不怕被问 volatile!

    反制面试官 | 14张原理图 | 再也不怕被问 volatile! 悟空 爱学习的程序猿,自主开发了Java学习平台.PMP刷题小程序.目前主修Java.多线程.SpringBoot.SpringCl ...

  9. 漂亮回答面试官struts2的原理

    众所周知,Struts2是个非常优秀的开源框架,我们能用Struts2框架进行开发,同时能快速搭建好一个Struts2框架,但我们是否能把Struts2框架的工作原理用语言表达清楚,你表达的原理不需要 ...

  10. JVM工作原理和特点(一些二逼的逼神面试官会问的问题)

    作为一种阅读的方式了解下jvm的工作原理 ps:(一些二逼的逼神面试官会问的问题) JVM工作原理和特点主要是指操作系统装入JVM是通过jdk中Java.exe来完毕,通过以下4步来完毕JVM环境. ...

随机推荐

  1. HTML元素如何按字符串原格式输出文本换行制表符信息

    只需给相应HTML元素添加 style="white-space: pre"

  2. 执行insmod提示 invalid module format

    内核版本和驱动版本不匹配: 1.假如内核版本是2018.3,驱动使用了另外一个版本,可能会出现这样的问题 2.内核和驱动版本一致,但内核进行了一些配置,导致驱动装不上,此时应该: make clean ...

  3. Golang 依赖注入设计哲学|12.6K 🌟 的依赖注入库 wire

    一.前言 线上项目往往依赖非常多的具备特定能力的资源,如:DB.MQ.各种中间件,以及随着项目业务的复杂化,单一项目内,业务模块也逐渐增多,如何高效.整洁管理各种资源十分重要. 本文从"术& ...

  4. Js 中的数组

    在js 中,数组就是对象,除了可以使用字面量语法[...]来声明数组外,它和其它对象没有什么区别.当创建一个数组['a', 'b', 'c']时,内部的实现形式如下: {     length: 3, ...

  5. yb课堂 订单列表开发,完结 《四十五》

    Order.vue <template> <div class="main"> <!--订单列表--> <div class=" ...

  6. C#皮肤美化

    关于Winform窗体美化,目前大致了解是有两种方式:第一种方式是重写Winform本身的控件,不过这需要非常熟悉控件的各个属性和事件并且要求具有很高的GDI绘图技术.第二种方式是借助第三方Winfo ...

  7. Mac Eclipse 常用快捷键汇总

    寻找类:shift+command+t 删除当前行:command+d 上移当前行代码:option+↑ 下移当前行代码:option+↓ 复制当前行至下一行:option+command+↓ 复制当 ...

  8. 通俗讲解promise

        JavaScript 中的 Promise 是一种特殊的对象,它用于解决异步编程中的复杂性问题,特别是回调的问题.我们可以把它比喻成现实生活中的一个"承诺": 想象一下,你 ...

  9. 基于 Impala 的高性能数仓实践之物化视图服务

    本文将主要介绍 NDH Impala 的物化视图实现. 接上篇,前两篇分别讲了执行引擎和虚拟数仓,它们是让一个 SQL 又快又好地执行的关键.但如果某些 SQL 过于复杂,比如多张大表进行 Join ...

  10. centos7安装pcntl扩展

    查看PHP扩展加载的目录php -i | grep extension_dir Centos下使用yum安装php默认是都不带pcntl扩展,需要安装扩展需要下载安装包,编译安装. 首先查看你的服务器 ...