volatile关键字有什么用?

1 个回答

  • 奥斯卡
    在漆黑的夜里,你需要一道光照亮夜行的路,我燃烧了我自己,但也造就了你我不能相拥。
    `volatile` 只提供**可见性**与**有序性**(内存屏障 + happens-before),不提供**互斥**;适合“读多写少、状态简单”的场景,比如停止标志、配置热更新指针、双重检查单例的指针。 # 它保证了什么 1. **可见性**:对 `volatile` 变量的写会立刻刷新到主内存;其他线程读到的是最新值。 2. **有序性**: * **写** `volatile` 具备 *release* 语义:写之前的普通读写**不能**被重排序到它之后。 * **读** `volatile` 具备 *acquire* 语义:读之后的普通读写**不能**被重排序到它之前。 * 规则:**对同一 `volatile` 变量的写 → 读 之间建立 happens-before**。 3. **(单次)原子性**:对单个变量的一次读/写是原子的(包括 `long/double`)。 > 但**复合操作**(如 `x++`, `check-then-act`)不是原子的,需要锁或原子类。 # 不保证什么 * 不保证**互斥**(多个线程可以同时修改)。 * 不保证**复合操作原子性**(如计数、自增、自减、基于旧值计算新值)。 * 不适合维护**跨多个字段的不变式**(需用锁)。 # 典型用法 ```java // 1) 停止标志 class Worker implements Runnable { private volatile boolean running = true; public void stop() { running = false; } public void run() { while (running) { /* do work */ } } } // 2) 安全发布不可变配置(用 volatile 引用指向新对象) class ConfigHolder { final int timeout; /*...*/ ConfigHolder(int t){timeout=t;} } class Service { private volatile ConfigHolder cfg = new ConfigHolder(100); void reload(int t){ cfg = new ConfigHolder(t); } // 发布新配置 int use(){ return cfg.timeout; } } // 3) 双重检查锁单例(DCL 必须配 volatile) class Singleton { private static volatile Singleton INSTANCE; static Singleton get() { if (INSTANCE == null) { // 第一次检查(快路径) synchronized (Singleton.class) { if (INSTANCE == null) INSTANCE = new Singleton(); // 屏障保证正确发布 } } return INSTANCE; } } ``` # 什么时候别用 `volatile` * 需要排他访问/临界区 → 用 `synchronized` / `ReentrantLock`。 * 需要原子自增/条件更新 → 用 `AtomicInteger/AtomicReference`(CAS)。 * 需要跨多个字段维持一致性 → 用锁或把字段封装进**不可变对象**,通过**volatile 引用**整体替换。 # 实现层面(HotSpot 简述) * 编译器与 CPU 会在 `volatile` 读写前后插入**内存屏障**: * 写:StoreStore + StoreLoad(release) * 读:LoadLoad + LoadStore(acquire) * 这样既**刷新/失效缓存**又**禁止重排**,保障上面的可见性与有序性。 # 对比速记 * `volatile`:可见性/有序性;无互斥;轻量。 * `synchronized`:可见性 + **互斥** + 可重入;语义强但可能阻塞。 * `Atomic*`:基于 CAS 的**原子更新**;适合计数/状态机。 **一句话记忆**:`volatile` 用来“看得见、顺序对”,不是用来“抢锁”的。
    发布于 2025-08-19 03:35 •IP属地 广东
    举报
    评论
    收藏