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. 又跳槽!3年java经验offer收割机的面试心得

    中厂->阿里->字节,成都->杭州->成都 系列文章目录和关于我 0.前言 笔者在不足两年经验的时候从成都一家金融科技中厂跳槽到杭州阿里淘天集团,又于今年5月份从杭州淘天跳槽到 ...

  2. 安装PHP5.6.20

    安装php的前提是安装了数据库和httpd!!!!!!!! 1 因为yum缺省安装的是PHP5.4,所以先要添加yum库 [root@svnhost ~]# rpm -Uvh https://mirr ...

  3. python调用微信JS_SDK及使用redis缓存access_token 和jsapi_ticket

    from flask import Flask, make_response,request import json import string import hashlib import rando ...

  4. 从零开始带你上手体验Sermant自定义插件开发

    本文分享自华为云社区<Sermant自定义插件开发上手体验>,作者:华为云开源. 一.研究缘由 由于目前我们所处的行业是汽车行业,项目上进行云服务的迁移时使用到了Sermant中的相关插件 ...

  5. 【Hadoop】Hadoop集群组件默认端口

    这里包含使用到的组件:HDFS, YARN, HBase, Hive, ZooKeeper: 组件 节点 默认端口 配置 用途说明 HDFS DataNode 50010 dfs.datanode.a ...

  6. vmware安装配置openwrt

    前言 OpenWrt是一个轻量的嵌入式linux系统,功能十分强大. 现在将我在vmware上安装OpenWrt的过程简单记录下来,以备后续参考. 环境准备 vmware软件 已经安装好的一个vmwa ...

  7. yb课堂 视频详情页模块开发《三十八》

    CourseDetail基础模块开发 CourseDetail模块开发,拆分组件 CourseDetail.vue Header.vue Course.vue Tab.vue Summary.vue ...

  8. 用这开源小书学 Docker,香!

    > 最新.全面.通俗.可多端阅读的 Docker 教程小书.>> 编程导航开源仓库:https://github.com/liyupi/code-navDocker 可以说是一个改变 ...

  9. MySQL ibdata1文件太大的解决办法

    在MySQL数据库中,如果不指定innodb_file_per_table=1参数单独保存每个表的数据,MySQL的数据都会存放在ibdata1文件里,时间久了这个文件就会变的非常大. 下面是参考网上 ...

  10. TIER 1: Crocodile

    TIER 1: Crocodile nmap 在前几次练习中,我们已经熟悉 nmap 扫描,我们在本次靶机中使用继续使用 nmap 进行扫描. 扩充我们的知识库:-sC 选项启用了 Nmap 的默认脚 ...