Java 中的 volatile 关键字
Java volatile 关键字提供可见性和有序性保证,但不保证原子性。它通过内存屏障实现线程间通信,适用于状态标志、双重检查锁定单例模式及安全发布场景。与 synchronized 相比,volatile 性能开销更小且不阻塞线程,但无法处理复合操作如 i++。建议优先使用不可变对象或原子类(如 AtomicInteger)处理计数等原子需求,仅在明确理解语义时使用 volatile。

Java volatile 关键字提供可见性和有序性保证,但不保证原子性。它通过内存屏障实现线程间通信,适用于状态标志、双重检查锁定单例模式及安全发布场景。与 synchronized 相比,volatile 性能开销更小且不阻塞线程,但无法处理复合操作如 i++。建议优先使用不可变对象或原子类(如 AtomicInteger)处理计数等原子需求,仅在明确理解语义时使用 volatile。

volatile 关键字详解volatile 是 Java 中用于修饰变量的关键字,它提供了一种轻量级的线程间通信机制。与 synchronized 相比,volatile 不会引起线程上下文切换和调度,因此性能开销更小。然而,其同步能力有限,使用不当容易产生线程安全问题。
volatile 变量的值时,该值会立即被强制刷新到主内存volatile 变量的读写操作进行指令重排序volatile 写操作之前的任何读写操作不会被重排序到写之后volatile 读操作之后的任何读写操作不会被重排序到读之前volatile无法保证复合操作的原子性(如 i++)[普通写/读操作] → [StoreStore 屏障] → [volatile 写] → [StoreLoad 屏障]
[volatile 读] → [LoadLoad 屏障] → [LoadStore 屏障] → [普通写/读操作]
内存屏障的作用:
public class ShutdownHandler {
private volatile boolean shutdownRequested = false;
public void shutdown() {
shutdownRequested = true;
}
public void doWork() {
while (!shutdownRequested) {
// 执行任务
}
}
}
public class Singleton {
// 必须使用 volatile 防止指令重排序
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
// 第一次检查:避免不必要的同步
synchronized (Singleton.class) {
if (instance == null) {
// 第二次检查:确保单例
// 创建对象分为三步(无 volatile 可能重排序):
// 1. 分配内存空间
// 2. 初始化对象
// 3. 将引用指向内存地址
// volatile 确保 2 在 3 之前完成
instance = new Singleton();
}
}
}
return instance;
}
}
public class Resource {
private volatile Resource resource;
public Resource getResource() {
if (resource == null) {
synchronized (this) {
if (resource == null) {
resource = new Resource();
}
}
}
return resource;
}
}
public class SensorReader {
private volatile double currentTemperature;
// 一个线程定期更新温度
public void updateTemperature(double temp) {
currentTemperature = temp;
}
// 多个线程同时读取最新的温度值
public double getTemperature() {
return currentTemperature;
}
}
volatile 与 synchronized 详细对比| 特性 | volatile | synchronized |
|---|---|---|
| 原子性 | 仅保证单个读/写操作的原子性 不保证复合操作(如 i++)的原子性 | 保证整个代码块/方法的原子性 |
| 可见性 | 保证变量对所有线程立即可见 | 保证变量对所有线程可见 |
| 有序性 | 禁止指令重排序 (通过内存屏障实现) | 保证有序性 (但允许同步块内重排序) |
| 阻塞性 | 非阻塞机制 线程不会挂起 | 阻塞机制 获取不到锁的线程会挂起等待 |
| 性能 | 轻量级,性能开销小 (仅内存屏障开销) | 重量级,性能开销较大 (涉及锁竞争、上下文切换) |
| 作用范围 | 变量级别 | 代码块或方法级别 |
| 适用场景 | 状态标志、一次性发布 | 复杂同步逻辑、需要原子性的复合操作 |
public class Counter {
private volatile int count = 0;
// 线程不安全!count++ 不是原子操作
public void increment() {
count++; // 实际包含:读 → 加 1 → 写 三个步骤
}
}
// 方案 1:使用 synchronized(适合复杂同步)
public class SynchronizedCounter {
private int count = 0;
public synchronized void increment() {
count++;
}
}
// 方案 2:使用原子类(推荐,性能更好)
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicCounter {
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet(); // CAS 操作,保证原子性
}
public int getCount() {
return count.get();
}
}
// 方案 3:使用 volatile + CAS(高级用法)
public class VolatileCASCounter {
private volatile int count = 0;
private static final sun.misc.Unsafe UNSAFE = // 获取 Unsafe 实例
public void increment() {
int current;
do {
current = count;
} while (!UNSAFE.compareAndSwapInt(this, OFFSET, current, current + 1));
}
}
volatile,设计为不可变对象volatile 适用场景volatile 变量不应依赖于其他变量,也不应被其他变量依赖java.util.concurrent.atomic 包下的原子类volatile 变量的读写操作建立了 happens-before 关系:
volatile 变量的写操作 happens-before 于后续对该变量的读操作synchronized 的释放锁 happens-before 于获取锁的语义类似// volatile 变量访问 vs 普通变量访问
volatile int vCounter = 0;
int counter = 0;
// volatile 访问有约 10-20% 的性能损耗
// 但相比 synchronized 的数千倍性能损耗可忽略不计
volatile 修饰会增加内存屏障开销volatile 适合与 final 结合,用于安全发布ThreadLocal 避免共享变量在 Java 5 之前,volatile 只保证可见性,不保证有序性。JSR-133(Java 内存模型修订)强化了 volatile 的语义,通过内存屏障同时保证了可见性和有序性。
volatile 是 Java 并发编程中的重要工具,但理解其局限性至关重要:
适合使用 volatile 的场景:
不适合使用 volatile 的场景:
黄金法则: 当您不确定是否需要使用 volatile 时,优先考虑使用更高层次的并发工具(如 java.util.concurrent 包中的类),这些工具通常封装了更安全、更高效的并发实现。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online