欢迎光临
我们一直在努力

一文彻底搞懂:volatile 与 CAS 核心原理、场景、实战

大家在 Java 并发编程中,经常被 volatile 和 CAS 搞晕: 什么时候用 volatile?什么时候用 CAS? volatile 能保护集合吗? CAS 能实现无锁安全的多读多写吗?

这篇文章用最简单的代码 + 最清晰的结论,一次性讲透。


一、volatile 到底能干嘛?

1.1 核心能力

  • 保证可见性:一个线程修改,其他线程立刻看到

  • 保证单次读、单次赋值原子性

  • 不保证复合操作原子性(i++、if + 操作 + 赋值 都不行)

  • 1.2 最经典用法:状态开关(一读一写)

    public class Test {
    private volatile boolean stop = false;

    // 写线程(单个)
    public void stop() {
    stop = true;
    }

    // 读线程(单个/多个)
    public void run() {
    while (!stop) {
    // 业务逻辑
    }
    }
    }

    ✅ 安全 ✅ 无锁 ✅ volatile 天生支持

    class Singleton {
    private static volatile Singleton instance; // volatile 关键

    public static Singleton getInstance() {
    if (instance == null) { // 第一次检查(无锁)
    synchronized (Singleton.class) { // 加锁!!!
    if (instance == null) { // 第二次检查(有锁)
    instance = new Singleton();
    }
    }
    }
    return instance;
    }
    }


    二、volatile 做控制器:保护其他变量(一读一写)

    volatile 可以作为开关,安全保护另一个共享变量(如 List)。

    规则:

    • 写线程:先写数据 → 最后修改 volatile

    • 读线程:先判断 volatile → 再读数据

    • 只能一读一写

    public class SafeDemo {
    private volatile boolean initialized = false;
    private List<String> dataList = new ArrayList<>();

    // 写线程(仅1个)
    public void writer() {
    if (!initialized) {
    dataList.clear();
    dataList.add("data1");
    dataList.add("data2");
    initialized = true;
    }
    }

    // 读线程(仅1个)
    public void reader() {
    if (initialized) {
    System.out.println(dataList.size());
    initialized = false;
    }
    }
    }

    ✅ 无锁安全 ✅ volatile 控制 + 共享变量保护 ❌ 不能多读(多个读线程会并发冲突)


    三、volatile + List 正确用法:替换引用

    volatile 只保证引用赋值可见 + 原子。

    正确姿势:

    • 写线程:新建 List → 赋值给 volatile

    • 读线程:只读取引用

    • 单写 + 多读安全

    private volatile List<String> list = new ArrayList<>();

    // 写线程(唯一写)
    void write() {
    List<String> newList = new ArrayList<>();
    newList.add("a");
    newList.add("b");
    list = newList; // 安全
    }

    // 读线程(多个)
    void read() {
    List<String> tmp = list;
    for (String s : tmp) {
    System.out.println(s);
    }
    }

    ✅ 无锁、安全、高效


    四、CAS 是什么?比 volatile 强在哪?

    4.1 核心区别

    • volatile:保证可见性、单次读写原子

    • CAS:保证单个变量复合操作原子性(++、if+set)

    4.2 CAS 能做什么

    • 计数器 incrementAndGet()

    • 条件更新 compareAndSet(expect, update)

    • 无锁自旋锁

    • 保护其他变量实现多读多写


    五、CAS 实战:无锁实现 List 多读多写

    利用 AtomicBoolean 做无锁互斥:

    • 同一时间只有一个线程能写

    • 其他写线程自旋等待

    • 读线程必须等写完成

    public class SafeListHandler {
    private List<String> list = new ArrayList<>();
    private final AtomicBoolean writing = new AtomicBoolean(false);

    // 多写安全
    public void write(String data) {
    while (!writing.compareAndSet(false, true)) {
    }

    try {
    list.add(data);
    } finally {
    writing.set(false);
    }
    }

    // 多读安全
    public List<String> read() {
    while (writing.get()) {
    }
    return new ArrayList<>(list);
    }
    }

    ✅ 完全无锁 ✅ 多读多写安全 ✅ List 被安全保护


    六、终极总结(面试 / 工作必背)

    volatile

    • 支持:单次读、单次赋值原子

    • 支持:一读一写、多读一写

    • 可做控制器:保护其他变量(只能一读一写)

    • 不支持:复合操作、多写

    CAS

    • 支持:复合操作原子性(++、条件赋值)

    • 支持:多读多写(单变量)

    • 可做无锁自旋锁:保护其他变量(多读多写)

    • 不支持:多变量同时原子

    一句话口诀

    • volatile:单写多读,无锁可见

    • CAS:单变量原子,无锁互斥

    • 想无锁安全多读多写:用 CAS 做自旋锁

    • volatile即使忙等待,也不能多读多写,因为不能解决原子性GETSET

    因为写只能同时一个在操作,只有原子性 get/set 的变量,才能安全多写;

    只要不是纯 get/set,多写必须用 CAS 或锁。

    中断例外,中断没有并发问题;

    volatile 和 cas 都是用于单变量。

    赞(0)
    未经允许不得转载:171主机测评 » 一文彻底搞懂:volatile 与 CAS 核心原理、场景、实战
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址