面试官:volatile如何保证可见性的,具体如何实现?
写在开头
在之前的几篇博文中,我们都提到了 volatile 关键字,这个单词中文释义为:不稳定的,易挥发的,在Java中代表变量修饰符,用来修饰会被不同线程访问和修改的变量,对于方法,代码块,方法参数,局部变量以及实例常量,类常量多不能进行修饰。
自JDK1.5之后,官网对volatile进行了语义增强,这让它在Java多线程领域越发重要!因此,我们今天就抽一晚上时间,来学一学这个关键字,首先,我们从标题入手,思考这样的一个问题:
volatile如何保证可见性,具体如何实现的?
带着疑问,我们继续往下阅读!
volatile如何保证可见性
volatile保证了不同线程对共享变量进行操作时的可见性,即一个线程修改了共享变量的值,共享变量修改后的值对其他线程立即可见。
我们先通过之前写的一个小案例来感受一下什么是可见性问题:
【代码示例1】
public class Test {
//是否停止 变量
private static boolean stop = false;
public static void main(String[] args) throws InterruptedException {
//启动线程 1,当 stop 为 true,结束循环
new Thread(() -> {
System.out.println("线程 1 正在运行...");
while (!stop) ;
System.out.println("线程 1 终止");
}).start();
//休眠 1 秒
Thread.sleep(1000);
//启动线程 2, 设置 stop = true
new Thread(() -> {
System.out.println("线程 2 正在运行...");
stop = true;
System.out.println("设置 stop 变量为 true.");
}).start();
}
}
输出:
线程 1 正在运行...
线程 2 正在运行...
设置 stop 变量为 true.
原因:
我们会发现,线程1运行起来后,休眠1秒,启动线程2,可即便线程2把stop设置为true了,线程1仍然没有停止,这个就是因为 CPU 缓存导致的可见性导致的问题。线程 2 设置 stop 变量为 true,线程 1 在 CPU 1上执行,读取的 CPU 1 缓存中的 stop 变量仍然为 false,线程 1 一直在循环执行。

那这个问题怎么解决呢?很好解决!我们排volatile上场可以秒搞定,只需要给stop变量加上volatile修饰符即可!
【代码示例2】
//给stop变量增加volatile修饰符
private static volatile boolean stop = false;
输出:
线程 1 正在运行...
线程 2 正在运行...
设置 stop 变量为 true.
线程 1 终止
从结果中看,线程1成功的读取到了线程而设置为true的stop变量值,解决了可见性问题。那volatile到底是什么让变量在多个线程之间保持可见性的呢?请看下图!

如果我们将变量声明为 volatile ,这就指示 JVM,这个变量是共享且不稳定的,每次使用它都到主存中进行读取,具体实现可总结为5步。
- 1️⃣在生成最低成汇编指令时,对volatile修饰的共享变量写操作增加Lock前缀指令,Lock 前缀的指令会引起 CPU 缓存写回内存;
- 2️⃣CPU 的缓存回写到内存会导致其他 CPU 缓存了该内存地址的数据无效;
- 3️⃣volatile 变量通过缓存一致性协议保证每个线程获得最新值;
- 4️⃣缓存一致性协议保证每个 CPU 通过嗅探在总线上传播的数据来检查自己缓存的值是不是修改;
- 5️⃣当 CPU 发现自己缓存行对应的内存地址被修改,会将当前 CPU 的缓存行设置成无效状态,重新从内存中把数据读到 CPU 缓存。
总结
其实volatile关键字不仅仅能解决可见性问题,还可以通过禁止编译器、CPU 指令重排序和部分 happens-before 规则,解决有序性问题,我们放在下一篇聊。
结尾彩蛋
如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏呀。原创不易,转载请联系Build哥!

如果您想与Build哥的关系更近一步,还可以关注“JavaBuild888”,在这里除了看到《Java成长计划》系列博文,还有提升工作效率的小笔记、读书心得、大厂面经、人生感悟等等,欢迎您的加入!

面试官:volatile如何保证可见性的,具体如何实现?的更多相关文章
- Volatile 只保证可见性,并不保证原子性
[尊重原创,转载请注明出处]http://blog.csdn.net/guyuealian/article/details/52525724 在说明Java多线程内存可见性之前,先来简单了解一下J ...
- 面试官:volatile关键字用过吧?说一下作用和实现吧
volatile 可见性的本质类似于CPU的缓存一致性问题,线程内部的副本类似于告诉缓存区 面试官:volatile关键字用过吧?说一下作用和实现吧 https://blog.csdn.net/ ...
- Volatile如何保证线程可见性之总线锁、缓存一致性协议
基础知识回顾 下图给出了假想机的基本设计.中央处理单元(CPU)是进行算术和逻辑操作的部件,包含了有限数量的存储位置--寄存器(register),一个高频时钟.一个控制单元和一个算术逻辑单元. 时钟 ...
- 面试高峰期,如何应对面试官的jvm刁难,特写一篇jvm面经(第一部)
已经进入三月份,正所谓金三银四,正是一年最好的招聘期,想必我的公号粉丝们一定有不少想要跳槽的吧,哈哈,/**偷偷告诉你们其实小编也准备跳槽*/(我要加个注释,被老板知道可就完蛋了),说到面试,想必大家 ...
- 面试官:说一下Synchronized底层实现,锁升级的具体过程?
面试官:说一下Synchronized底层实现,锁升级的具体过程? 这是我去年7,8月份面试的时候被问的一个面试题,说实话被问到这个问题还是很意外的,感觉这个东西没啥用啊,直到后面被问了一波new O ...
- Java线程-volatile不能保证原子性
下面是一共通过volatile实现原子性的例子: 通过建立100个线程,计算number这个变量最后的结果. package com.Sychronized; public class Volatil ...
- Java中volatile如何保证long和double的原子性操作
原创转载请注明出处:https://www.cnblogs.com/agilestyle/p/11426473.html 关键字volatile的主要作用是使变量在多个线程间可见,但无法保证原子性,对 ...
- 为什么volatile能保证有序性不能保证原子性
对于内存模型的三大特性:有序性.原子性.可见性. 大家都知道volatile能保证可见性和有序性但是不能保证原子性,但是为什么呢? 一.原子性.有序性.可见性 1.原子性: (1)原子的意思代表着-- ...
- 面试官最爱的volatile关键字
在Java相关的岗位面试中,很多面试官都喜欢考察面试者对Java并发的了解程度,而以volatile关键字作为一个小的切入点,往往可以一问到底,把Java内存模型(JMM),Java并发编程的一些特性 ...
- Java面试官最常问的volatile关键字
在Java相关的职位面试中,很多Java面试官都喜欢考察应聘者对Java并发的了解程度,以volatile关键字为切入点,往往会问到底,Java内存模型(JMM)和Java并发编程的一些特点都会被牵扯 ...
随机推荐
- [JVM] JVM的类加载机制
JVM的类加载 首先我们来看下Java虚拟机的类加载过程: 如上图. 当JVM需要用到某个类的时候,虚拟机会加载它的 .class 文件.加载了相关的字节码信息之后,会常见对应的 Class 对象,这 ...
- 【.NET】聊聊 IChangeToken 接口
由于两个月的奋战,导致很久没更新了.就是上回老周说的那个产线和机械手搬货的项目,好不容易等到工厂放假了,我就偷偷乐了.当然也过年了,老周先给大伙伴们拜年了,P话不多讲,就祝大家身体健康.生活愉快.其实 ...
- NC23501 小A的回文串
题目链接 题目 题目描述 小A非常喜欢回文串,当然我们都知道回文串这种情况是非常特殊的.所以小A只想知道给定的一个字符串的最大回文子串是多少,但是小A对这个结果并不是非常满意.现在小A可以对这个字符串 ...
- SSD 简介—— NAND 芯片介绍
制作 存储芯片的制作和其他芯片制作大致相同,从沙子中提取单晶硅制作晶圆再封装芯片. 闪存芯片从架构上分为NOR和NAND NOR Flash的source line把每个cell都并联起来,而NAND ...
- android架构组件Lifecycle
Lifecycle 组件指的是 android.arch.lifecycle 包下提供的各种类与接口,可以让开发者构建能感知其他组件(主要指Activity .Fragment)生命周期(lifecy ...
- 从零开始手写 mybatis(二)mybatis interceptor 插件机制详解
前景回顾 第一节 从零开始手写 mybatis(一)MVP 版本 中我们实现了一个最基本的可以运行的 mybatis. 常言道,万事开头难,然后中间难. mybatis 的插件机制是 mybatis ...
- Swoole从入门到入土(25)——多进程[进程间无锁计数器]
Atomic 是 Swoole 底层提供的原子计数操作类,可以方便整数的无锁原子增减.原子计数器有如下特点: - 使用共享内存,可以在不同的进程之间操作计数 - 基于 gcc/clang 提供的 CP ...
- centos7安装mailx
1.安装mailx yum install mailx -y 2.配置证书 mkdir -p /root/.certs echo -n | openssl s_client -connect smtp ...
- Failed to bind properties under ” to com.zaxxer.hikari.HikariDataSource
1.问题说明 今天配置spring boot多数据源,同时用到了oracle和postgresql,结果配置完毕后启动报这个错. 2.原因分析 忘记添加postgresql驱动了!!! 3.解决方案 ...
- 运用 Argo Workflows 协调 CI/CD 流水线
Argo Workflows 是一个开源的容器原生工作流引擎,用于协调 CI/CD 在 Kubernetes 中的运作.它以 Kubernetes 自定义资源(CRD)的形式实现,使开发人员能够创建自 ...