每日一问:谈谈 volatile 关键字
这是 wanAndroid 每日一问中的一道题,下面我们来尝试解答一下。
讲讲并发专题 volatile,synchronize,CAS,happens before, lost wake up
为了本系列的「短平快」,今天我们就来第一个主角:volatile。
保证内存可见性
前面我们讲到:Java 内存模型分为了主内存和工作内存两部分,其规定程序所有的变量都存储在主内存中,每条线程还有自己的工作内存,线程的工作内存中保存了该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作(赋值、读取等)都必须在工作内存中进行,而不能直接读取主内存中的变量。不同线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递都必须经过主内存的传递来完成。

这样就会存在一个情况,工作内存值改变后到主内存更新一定是需要一定时间的,所以可能会出现多个线程操作同一个变量的时候出现取到的值还是未更新前的值。
这样的情况我们通常称之为「可见性」,而我们加上 volatile 关键字修饰的变量就可以保证对所有线程的可见性。
这里的可见性是什么意思呢?当一个线程修改了变量的值,新的值会立刻同步到主内存当中。而其他线程读取这个变量的时候,也会从主内存中拉取最新的变量值。
为什么 volatile 关键字可以有这样的特性?这得益于 Java 语言的先行发生原则(happens-before)。简单地说,就是先执行的事件就应该先得到结果。
但是! volatile 并不能保证并发下的安全。
Java 里面的运算并非原子操作,比如 i++ 这样的代码,实际上,它包含了 3 个独立的操作:读取 i 的值,将值加 1,然后将计算结果返回给 i。这是一个「读取-修改-写入」的操作序列,并且其结果状态依赖于之前的状态,所以在多线程环境下存在问题。
要解决自增操作在多线程下线程不安全的问题,可以选择使用 Java 提供的原子类,如
AtomicInteger或者使用synchronized同步方法。原子性:在 Java 中,对基本数据类型的变量的读取和赋值操作是原子性操作,即这些操作是不可被中断的,要么执行,要么不执行。也就是说,只有简单的读取、赋值(而且必须是将数字赋值给某个变量)才是原子操作。(变量之间的相互赋值不是原子操作,比如
y = x,实际上是先读取x的值,再把读取到的值赋值给y写入工作内存)
禁止指令重排
最开始看到「指令重排」这个词语的时候,我也是一脸懵逼。后面看了相关书籍才知道,处理器为了提高程序效率,可能对输入代码进行优化,它不保证各个语句的执行顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。
指令重排是一把双刃剑,虽然优化了程序的执行效率,但是在某些情况下,却会影响到多线程的执行结果。比如下面的代码:
boolean contextReady = false;
//在线程A中执行:
context = loadContext(); // 步骤 1
contextReady = true; // 步骤 2
//在线程B中执行:
while(!contextReady ){
sleep(200);
}
doAfterContextReady (context);
以上程序看似没有问题。线程 B 循环等待上下文 context 的加载,一旦 context 加载完成,contextReady == true 的时候,才执行 doAfterContextReady 方法。
但是,如果线程 A 执行的代码发生了指令重排,也就是上面的步骤 1 和步骤 2 调换了顺序,那线程 B 就会直接跳出循环,直接执行 doAfterContextReady() 方法导致出错。
而 volatile 采用「内存屏障」这样的 CPU 指令就解决这个问题,不让它指令重排。
使用场景
从上面的总结来看,我们非常容易得出 volatile 的使用场景:
- 运行结果并不依赖变量的当前值,或者能够确保只有单一的线程修改变量的值。
- 变量不需要与其他的状态变量共同参与不变约束。
比如下面的场景,就很适合使用 volatile 来控制并发,当 shutdown() 方法调用的时候,就能保证所有线程中执行的 work() 立即停下来。
volatile boolean shutdownRequest;
private void shutdown(){
shutdownRequest = true;
}
private void work(){
while (!shutdownRequest){
// do something
}
}
总结
说了这么多,其实对于 volatile 我们只需要知道,它主要特性:保证可见性、禁止指令重排、解决 long 和 double 的 8 字节赋值问题。
还有一个比较重要的是:它并不能保证并发安全,不要和 synchronize 混淆。
细心的你还会发现,在 Kotlin 语言中,其实是没有
volatile和synchronize这样的关键字的,那 Kotlin 是怎么处理并发问题的呢?感兴趣的一定要去看看。
文章参考:
漫画:什么是volatile关键字?(整合版)
《深入理解 Java 虚拟机》
每日一问:谈谈 volatile 关键字的更多相关文章
- Java面试官最爱问的volatile关键字
在Java的面试当中,面试官最爱问的就是volatile关键字相关的问题.经过多次面试之后,你是否思考过,为什么他们那么爱问volatile关键字相关的问题?而对于你,如果作为面试官,是否也会考虑采用 ...
- Java面试官最常问的volatile关键字
在Java相关的职位面试中,很多Java面试官都喜欢考察应聘者对Java并发的了解程度,以volatile关键字为切入点,往往会问到底,Java内存模型(JMM)和Java并发编程的一些特点都会被牵扯 ...
- 面试必问的volatile关键字
原文: 卡巴拉的树 https://juejin.im/post/5a2b53b7f265da432a7b821c 在Java相关的岗位面试中,很多面试官都喜欢考察面试者对Java并发的了解程度, ...
- 谈谈volatile关键字以及常见的误解
转载请保留以下声明 作者:赵宗晟 出处:https://www.cnblogs.com/zhao-zongsheng/p/9092520.html 近期看到C++标准中对volatile关键字的定义, ...
- JAVA多线程基础学习三:volatile关键字
Java的volatile关键字在JDK源码中经常出现,但是对它的认识只是停留在共享变量上,今天来谈谈volatile关键字. volatile,从字面上说是易变的.不稳定的,事实上,也确实如此,这个 ...
- JUC 并发编程--05, Volatile关键字特性: 可见性, 不保证原子性,禁止指令重排, 代码证明过程. CAS了解么 , ABA怎么解决, 手写自旋锁和死锁
问: 了解volatile关键字么? 答: 他是java 的关键字, 保证可见性, 不保证原子性, 禁止指令重排 问: 你说的这三个特性, 能写代码证明么? 答: .... 问: 听说过 CAS么 他 ...
- JAVA多线程学习- 三:volatile关键字
Java的volatile关键字在JDK源码中经常出现,但是对它的认识只是停留在共享变量上,今天来谈谈volatile关键字. volatile,从字面上说是易变的.不稳定的,事实上,也确实如此,这个 ...
- 【面试普通人VS高手系列】volatile关键字有什么用?它的实现原理是什么?
一个工作了6年的Java程序员,在阿里二面,被问到"volatile"关键字. 然后,就没有然后了- 同样,另外一个去美团面试的工作4年的小伙伴,也被"volatile关 ...
- 围绕一个 volatile 关键字居然可以问出来 16 个问题
对于 Java 每次面试就会想到多线程,多线程问题基本跑不了要问一下 volalite 关键字,可是我万万没想到居然一个 volatile 关键字可以连续问题出来 16 个问题!看下你能回答出来几个? ...
随机推荐
- 干货最新版 Spring Boot2.1.5 教程+案例合集
最近发了一系列的 Spring Boot 教程,但是发的时候没有顺序,有小伙伴反映不知道该从哪篇文章开始看起,刚好最近工作告一个小小段落,松哥就把这些资料按照学习顺序重新整理了一遍,给大家做一个索引, ...
- Prometheus K8S部署
Prometheus K8S部署 部署方式:https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/prometheus ...
- 基于 HTML5 WebGL 的 3D 智慧隧道漫游巡检
前言 这次为大家展示的是通过 HT for Web 灵活的图型化编辑工具打造的智慧隧道监控系统.通过 HTML5 技术实现了桌面和移动端的跨平台性,同时现实了可视化运维. 这次主要跟大家分享里面的漫游 ...
- Web应急:门罗币恶意挖矿
门罗币(Monero 或 XMR),它是一个非常注重于隐私.匿名性和不可跟踪的加密数字货币.只需在网页中配置好js脚本,打开网页就可以挖矿,是一种非常简单的挖矿方式,而通过这种恶意挖矿获取数字货币是黑 ...
- MySQL各类型字段可定义最大宽度
今天浏览mysql的官网文档,无意中看到如图划线部分一句话,引起了我的兴趣,所以决定做实验官方所言. 条例1.创建数据表时,所有字段定义时"宽度之和"不得超过65535字节: 条例 ...
- 【07】Jenkins:流水线(Pipeline)
写在前面的话 个人认为 Pipeline 在 Jenkins 中算是一个优化性功能,它能够将我们的构建服务的整个过程流程化,这意味着当我们在执行到某一步的时候,可以添加询问,提示我们是否继续运行下一步 ...
- .net架构的浅谈
,net的架构有以下几种 1.两层架构:UI + 数据层 2.三层架构:UI + 业务层 + 数据层 3.三层 + 接口层 (把相关的业务层抽象成接口,下层来实现接口,中层是依赖) 4.三层 + 接口 ...
- 机甲大师S1机器人编程学习,Windows 10 安装Scratch和简单实例学习
机甲大师S1支持 Scratch 3.0以上版本. Scratch官方网址:https://scratch.mit.edu/ 最新版本为3.4.0 今天在Windows 10上,安装最新版本. 1. ...
- OO第四单元作业小结
一.本单元两次作业的架构设计 1.第一次作业 整体思路:以class为核心,建立MyInterface.MyAttribute.MyOperation.MyAssociation四个类分别储存每个类的 ...
- Eureka2.0集群环境
当有2个eureka注册中心构建eureka注册中心集群时,1:微服务的注册中心地址要写2个eureka注册中心的地址,以逗号分隔2:其中1个eureka注册中心A保留有微服务的注册信息,作为主注册中 ...