在 Java 中,volatile 关键字的作用主要有以下两点:

1. 保证变量的可见性

  • 当一个变量被 volatile 修饰时,它的值会直接存储在主内存中,而不是仅存在线程的工作内存(CPU 缓存)中。

  • 这样,当一个线程修改 volatile 变量的值时,其他线程立即能够看到最新的值,而不会读取过期的数据。

2. 禁止指令重排序

  • 现代 CPU 和 JIT 编译器为了优化性能,可能会对代码指令进行重排序,但 volatile 变量的读/写操作不会被重排序到其他非 volatile 变量的读/写操作之前或之后。

  • 这可以在某些情况下保证有序性,比如在双重检查锁单例模式(DCL)中,防止未初始化对象被错误读取。


⚠️ 但是 volatile 不能保证原子性

volatile 只保证了可见性和有序性,但不能保证对变量的操作是原子性的。例如,volatile 不能保证 count++ 操作的线程安全,因为 count++ 其实是三步操作:

  1. 读取 count 的值

  2. 计算 count + 1

  3. 将新值写回 count

这三步不是一个原子操作,可能会导致线程安全问题。

如果要保证原子性,可以使用 synchronizedAtomicIntegerLock 代替 volatile


✅ 使用 volatile 的典型场景:

  1. 多线程状态标记(比如 boolean flag

  2. 单例模式(DCL 方式)private static volatile Singleton instance

  3. Double-Checked Locking 机制,防止指令重排序导致的对象未完全初始化问题


示例代码:

1️⃣ 适合使用 volatile线程间的可见性

class VolatileExample {
    private static volatile boolean flag = false;

    public static void main(String[] args) {
        new Thread(() -> {
            while (!flag) {
                // 等待 flag 变为 true
            }
            System.out.println("线程 1 结束");
        }).start();

        try { Thread.sleep(1000); } catch (InterruptedException ignored) {}

        new Thread(() -> {
            flag = true; // 修改 flag,另一个线程会立刻感知到
            System.out.println("线程 2 修改了 flag");
        }).start();
    }
}

2️⃣ 不适合使用 volatile不能保证原子性

class VolatileAtomicityTest {
    private static volatile int count = 0;

    public static void main(String[] args) throws InterruptedException {
        Runnable task = () -> {
            for (int i = 0; i < 10000; i++) {
                count++;  // 不是原子操作,可能丢失更新
            }
        };

        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);
        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println("最终 count 值:" + count);  // 结果可能小于 20000
    }
}

✅ 解决方法:使用 AtomicInteger

import java.util.concurrent.atomic.AtomicInteger;

class AtomicExample {
    private static AtomicInteger count = new AtomicInteger(0);

    public static void main(String[] args) throws InterruptedException {
        Runnable task = () -> {
            for (int i = 0; i < 10000; i++) {
                count.incrementAndGet(); // 线程安全的自增
            }
        };

        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);
        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println("最终 count 值:" + count);  // 正确输出 20000
    }
}

总结:

关键字/机制

可见性

原子性

防止指令重排序

volatile

synchronized

AtomicInteger

如果只是保证可见性,可以用 volatile,但如果涉及复合操作(如++),要用 synchronizedAtomic 类。