程序员求职经验分享与学习资料整理平台

网站首页 > 文章精选 正文

Java 中的 volatile 关键字(java volatile 例子)

balukai 2025-06-28 12:13:44 文章精选 2 ℃

volatile 是 Java 中的一个关键字,用于修饰变量,主要解决多线程环境下的内存可见性问题和指令重排序问题。

主要作用

  1. 保证可见性:当一个线程修改了 volatile 变量的值,新值会立即被刷新到主内存中,其他线程读取时会直接从主内存读取最新值。
  2. 禁止指令重排序:防止 JVM 对 volatile 变量相关的代码进行指令重排序优化。

基本用法


public class SharedObject {

public volatile int counter = 0;

}


volatile 的特性

1. 内存可见性保证

没有 volatile 修饰时可能出现的问题:


// 没有 volatile 修饰

boolean running = true;


void work() {

while(running) {

// 工作代码

}

}


void stop() {

running = false;

}


在这个例子中, stop() 方法修改 running 的值后, work() 方法的循环可能不会立即停止,因为工作线程可能在自己的工作内存中保留了 running 的旧值。

使用 volatile 可以解决:


volatile boolean running = true;


2. 禁止指令重排序

volatile 通过插入内存屏障来防止指令重排序,这在单例模式的双重检查锁定中很重要:


class Singleton {

private static volatile Singleton instance;

public static Singleton getInstance() {

if (instance == null) {

synchronized (Singleton.class) {

if (instance == null) {

instance = new Singleton();

}

}

}

return instance;

}

}


如果不使用 volatile, instance = new Singleton() 可能会被重排序,导致其他线程看到未完全初始化的对象。

volatile 的局限性

1. 不保证原子性:volatile 不能替代 synchronized,它不保证复合操作的原子性。

错误示例:


volatile int count = 0;

count++; // 这不是原子操作


count++ 实际上是读-改-写三个操作,volatile 不能保证这三个操作的整体原子性。

2. 不适用于依赖当前值的操作:如 count = count + 1 count++

volatile 适用场景

  • 状态标志位(如前面示例中的 running
  • 单次安全发布(如单例模式的双重检查锁定)
  • 独立观察(定期发布观察结果供程序使用)
  • "开销较低的读-写锁策略"(读远多于写的情况)

volatile 与 synchronized 比较

特性

volatile

synchronized

作用范围

变量

变量、代码块、方法

内存可见性

保证

保证

原子性

不保证

保证

阻塞

不会导致线程阻塞

可能导致线程阻塞

性能

更高

相对较低

底层原理

volatile 的实现依赖于 JVM 的内存屏障:

  • 写操作前插入 StoreStore 屏障
  • 写操作后插入 StoreLoad 屏障
  • 读操作前插入 LoadLoad 屏障
  • 读操作后插入 LoadStore 屏障

这些屏障保证了 volatile 变量的读写操作不会被重排序,并且写操作后会立即刷新到主内存。

最近发表
标签列表