Java synchronized 与 volatile

synchronized 提供互斥与可见性,volatile 提供可见性与有限有序性:何时用锁、何时用 volatile。

#type / concept #status / growing #tech / dev #resource / java

[!info] 关联笔记

Java synchronized 与 volatile

这个概念为什么出现

多线程共享可变状态时,不仅会“算错”,还会因缓存/重排序导致看见过期值synchronized 提供临界区互斥与内存语义;volatile 让单次读写具备可见性与特定有序性,但不提供复合操作原子性。

[!abstract] 一句话理解 synchronized 保证同一锁上互斥并建立 happens-before;volatile 保证可见性与部分有序,不适合 count++ 这类读改写。

最小可运行示例

场景:库存扣减必须互斥;开关标志用 volatile 发布

public class InventoryGuard {
    private int stock;
    private volatile boolean open = true; // 状态发布

    InventoryGuard(int stock) { this.stock = stock; }

    synchronized boolean reserve(int qty) {
        if (!open) return false;
        if (stock < qty) return false;
        stock -= qty; // 读改写需要互斥
        return true;
    }

    void close() { open = true; } // 示例:可见性发布(业务可设 false)
    void shutdown() { open = false; }

    synchronized int stock() { return stock; }

    public static void main(String[] args) throws Exception {
        InventoryGuard g = new InventoryGuard(1);
        Thread t1 = new Thread(() -> System.out.println("t1=" + g.reserve(1)));
        Thread t2 = new Thread(() -> System.out.println("t2=" + g.reserve(1)));
        t1.start(); t2.start();
        t1.join(); t2.join();
        System.out.println("left=" + g.stock());
    }
}

建议运行:

javac InventoryGuard.java && java InventoryGuard

期望输出(顺序可互换,但成功预留恰好一次):

t1=true
t2=false
left=0

或 t1/t2 对调。

结合场景再看三个关注点

  1. stock -= qty 必须在同一把锁内
  2. volatile 适合旗标/发布,不适合复合更新。
  3. 锁粒度影响性能与死锁风险。

核心概念与准确模型

1. synchronized

  • 实例方法锁 this
  • static 方法锁 Class 对象
  • 同步块可指定锁对象

2. volatile

  • 读看到最新写(在 JMM 规则下)
  • 不保证 i++ 原子
  • 适合独立状态旗标、安全发布某些引用

3. 与 JMM

完整 happens-before 规则见 内存模型

边界情况与反直觉行为

  1. 同步库不一致(有的路径加锁有的不加)= 等于没锁。
  2. 锁顺序相反 → 死锁。
  3. 在锁内做远程 IO → 吞吐崩坏。

常见误区

[!warning] 常见误区:变量加了 volatile 就线程安全 复合操作仍需锁或原子类。

工程实践

  1. 优先高阶并发工具/不可变/分区。
  2. 锁内逻辑尽量短。
  3. 私有 final 锁对象避免外部抢锁。
  4. 用测试与 jctools/并发测试思路验证(至少多线程复现)。

本节总结

  • 互斥用锁;可见性旗标可用 volatile
  • 读改写不是 volatile 的工作
  • 为 JUC 与 JMM 深化做准备

自测题

  1. 为什么 volatile int c; c++ 不安全?
  2. synchronized 方法与同步块差别?
参考答案
  1. c++ 是读-改-写三步,可交错。
  2. 方法锁固定为 this/Class;同步块可更细粒度选锁与范围。

延伸阅读与资料来源

资料类型支撑内容
Synchronization (Tutorial)教程synchronized
JLS volatile规范volatile 语义
创建于 2026/7/15 更新于 2026/7/15