阿里面试题:volatile 夺命 连环炮 !答垮了就挂,非常重要!
技术自由圈 2023-01-20 10:42 湖南
技术自由圈
疯狂创客圈(技术自由架构圈):一个 技术狂人、技术大神、高性能 发烧友 圈子。圈内一大波顶级高手、架构师、发烧友已经实现技术自由;另外一大波卷王,正在狠狠卷,奔向技术自由
150篇原创内容
公众号
本文是一道非常核心的大厂面试题,涉及 volatile 的夺命连环炮。
volatile 在高并发编程中非常常用,而底层原理又很复杂,如果答题垮掉,面试基本也就挂掉了。
所以,此文给大家奉上一个非常重要的 大厂 连环炮 面试题,大家收藏起来,慢慢吸收和消化吧。
此题的内容,收录于《尼恩Java面试宝典》 V30版
聊聊:volatile的作用?
volatile的作用就是,核心是两个:
- 「保证变量对所有线程可见性」。
- 「禁止指令重排」
但是
- 「不保证原子性」。
所以当面试官问你「volatile的作用或者特性」,都可以这么回答:
- 保证变量对所有线程可见性;
- 禁止指令重排序
- 不保证原子性
关于:重排序,可见性的底层原理,请参见:
《Java高并发核心编程(卷2):多线程、锁、JMM、JUC、高并发设计模式》
聊聊:volatile的典型场景
通常来说,使用volatile必须具备以下2个条件:
- 1)对变量的写操作不依赖于当前值
- 2)该变量没有包含在具有其他变量的不变式中
实际上,volatile场景一般就是
- 状态标志
- DCL单例模式
- CAS 轻量级乐观锁场景
场景1:状态标志场景
深入理解Java虚拟机,书中的例子:
Map configOptions;
char[] configText;
// 此变量必须定义为 volatile
volatile boolean initialized = false;
// 假设以下代码在线程 A 中运行
// 模拟读取配置信息, 当读取完成后将 initialized 设置为 true 以告知其他线程配置可用
configOptions = new HashMap();
configText = readConfigFile(fileName);
processConfigOptions(configText, configOptions);
initialized = true;
// 假设以下代码在线程 B 中运行
// 等待 initialized 为 true, 代表线程 A 已经把配置信息初始化完成
while(!initialized) {
sleep();
}
// 使用线程 A 中初始化好的配置信息
doSomethingWithConfig();
场景2:DCL单例模式
class Singleton{
private volatile static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if(instance==null) {
synchronized (Singleton.class) {
if(instance==null)
instance = new Singleton();
}
}
return instance;
}
}
场景3:CAS 轻量级乐观锁场景
JUC AQS源码中,其核心的成员 state (同步状态)、head (头指针)、tail (尾部指针), 都是通过CAS进行乐观锁修改的,都需要进行 volatile保证可见性。
同样,在高性能组件 caffeine 、 disruptor 源码中, 都是CAS + volatile 配合使用的
caffeine 、 disruptor 源码, 请参见 第24章、 第25章 视频。
聊聊:volatile的内存语义
- 当写一个 volatile 变量时,JMM 会把该线程对应的本地内存中的共享变量值刷新到主内存。
- 当读一个 volatile 变量时,JMM 会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。