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. spring boot jpa 进行通用多条件动态查询和更新 接口

    原因: jpa 没有类似于mybatis的那种 拼接sql的方式 想动态更新 需要使用 CriteriaUpdate的方式 去一直拼接,其实大多数场景只要传入一个非空实体类,去动态拼接sql 1.定义 ...

  2. 数据库学习(一)——DDL数据库定义语句

    定义数据库 创建数据库 使用CRETE DATABASE关键字,指定编码和排序格式 CREATE DATABASE mysqldb DEFAULT CHARACTER SET utf-8 DEFAUL ...

  3. 解决Vue中使用history路由模式出现404的问题

    背景 vue中默认的路由模式是hash,会出现烦人的符号#,如http://127.0.0.1/#/. 改为history模式可以解决这个问题,但是有一个坑是:强刷新.回退等操作会出现404. Vue ...

  4. 09-CentOS软件包管理

    简介 CentOS7使用rpm和yum来管理软件包. CentOS 8附带YUM包管理器v4.0.4版本,该版本现在使用DNF (Dandified YUM)技术作为后端.DNF是新一代的YUM,新的 ...

  5. Android无障碍自动化结合opencv实现支付宝能量自动收集

    Android无障碍服务可以操作元素,手势模拟,实现基本的控制.opencv可以进行图像识别.两者结合在一起即可实现支付宝能量自动收集.opencv用于识别能量,无障碍服务用于模拟手势,即点击能量. ...

  6. CLR via C# 笔记 -- 可空值类型(19)

    1. 值类型的变量永远不会为null,所有需要使用System.Nullable<T> 2. 操作数是null,结构是null:==.!=操作数都为null,则返回true:<.&g ...

  7. CLR via C# 笔记 -- 数组(16)

    1. 数组隐式继承 System.Array,所以数组是引用类型.变量包含的是对数组的引用,而不是包含数据本身的元素. 2. 数组协变性.将数组从一种类型转换为另一种类型. string[] sa = ...

  8. 配置 Gerrit 迁移

    https://gerrit.googlesource.com/plugins/replication/+doc/master/src/main/resources/Documentation/con ...

  9. 使用定时器:在logs目录,每两分钟产生一个文件

    1.使用定时器:在logs目录,每两分钟产生一个文件(文件格式为:201711151323.log.201711151323.log.2017111513xx.log ...思路:定时器定时调用she ...

  10. Spark3学习【基于Java】5. Spark-Sql联表查询JOIN

    大数据场景下,联表远比微小型关系型数据库中使用的频繁.网上有句话: 传统数据库单机模式做Join的场景毕竟有限,也建议尽量减少使用Join. 然而大数据领域就完全不同,Join是标配,OLAP业务根本 ...