← [ BACK_TO_NOTES ]

Java synchronized 与 volatile

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

#concept #growing #dev #java

[!info] related notes

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 (qty <= 0) throw new IllegalArgumentException("qty must be positive");
        if (!open) return false;
        if (stock < qty) return false;
        stock -= qty; // 读改写需要互斥
        return true;
    }

    void shutdown() { open = false; } // 发布停止标志,不等待已经通过检查的 reserve

    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. 停止标志不等于关闭屏障:shutdown 返回后,已经读到 open=true 的 reserve 仍可能完成。若要求返回时无在途预留,shutdown 也应获取与 reserve 相同的锁。

核心概念与准确模型

1. synchronized

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

2. volatile

  • 对同一 volatile 变量,写 happens-before 同步顺序中后续的读;还可发布该写之前的普通字段修改,但不把多个操作合成事务。\
  • 不保证 i++ 原子
  • 适合独立状态旗标、安全发布某些引用

3. 与 JMM

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

边界情况与反直觉行为

  1. 若同一共享状态有未同步的冲突访问,部分路径加锁不足以保证整体正确;需统一同步策略。\
  2. 锁顺序相反 → 死锁。
  3. 在锁内做远程 IO → 吞吐崩坏。

常见误区

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

工程实践

  1. 优先高阶并发工具/不可变/分区。
  2. 锁内逻辑尽量短。
  3. 私有 final 锁对象避免外部抢锁。
  4. 用同步关系证明正确性,再以并发测试辅助找反例;OpenJDK jcstress 是并发测试工具,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/10/6
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本