AtomicBoolean + CAS 机制通俗讲解

什么是 CAS?

CAS(Compare And Swap,比较并交换)是一种原子操作,就像是一个"智能开关"。

生活中的比喻

想象一个厕所门上的"有人/无人"指示牌:

传统方式(有问题):
1. 你看了一眼牌子:"无人"
2. 你准备去改牌子
3. 这时另一个人也看到"无人",也准备改牌子
4. 你们都改了牌子 → 两个人都进去了!❌

CAS 方式(安全):
1. 你看了一眼牌子:"无人"
2. 你对牌子说:"如果现在是'无人',就帮我改成'有人'"
3. 牌子会检查:如果确实还是"无人",就改成"有人",返回成功 ✅
4. 如果已经被别人改成了"有人",就返回失败 ❌

Java 中的 AtomicBoolean

AtomicBoolean 就是一个线程安全的布尔值,它提供了 CAS 操作。

核心方法:compareAndSet

AtomicBoolean flag = new AtomicBoolean(false);

// 尝试将 false 改成 true
boolean success = flag.compareAndSet(false, true);

if (success) {
    System.out.println("成功获取执行权!");
} else {
    System.out.println("已经有任务在运行了,本次跳过");
}

compareAndSet 的工作原理

public boolean compareAndSet(boolean expect, boolean update) {
    // 伪代码逻辑:
    // 1. 读取当前值
    // 2. 如果当前值 == expect(期望值)
    // 3.   则将当前值设置为 update(新值),返回 true
    // 4. 否则返回 false
    
    // 整个过程是原子的,不会被其他线程打断
}

实际应用场景

场景1:防止重复执行

public class TaskExecutor {
    private final AtomicBoolean isRunning = new AtomicBoolean(false);
    
    public void execute() {
        // 尝试获取执行权
        if (!isRunning.compareAndSet(false, true)) {
            System.out.println("任务正在执行中,跳过本次请求");
            return;
        }
        
        try {
            // 执行业务逻辑
            doWork();
        } finally {
            // 释放执行权
            isRunning.set(false);
        }
    }
}

执行流程:

时刻1: 线程A调用 execute()
       compareAndSet(false, true) → 成功,isRunning = true
       开始执行任务...

时刻2: 线程B调用 execute()
       compareAndSet(false, true) → 失败(当前是true)
       直接返回,不执行任务

时刻3: 线程A执行完毕
       finally 块中 isRunning.set(false)
       
时刻4: 线程C调用 execute()
       compareAndSet(false, true) → 成功,isRunning = true
       开始执行任务...

场景2:单次初始化

public class Singleton {
    private static final AtomicBoolean initialized = new AtomicBoolean(false);
    
    public void init() {
        // 只有第一个调用的线程能成功
        if (!initialized.compareAndSet(false, true)) {
            return; // 已经初始化过了
        }
        
        // 执行初始化逻辑
        doInit();
    }
}

CAS vs synchronized

特性CAS (AtomicBoolean)synchronized
实现方式硬件级别的原子指令JVM 层面的锁机制
性能无锁竞争时更快有锁的开销
阻塞非阻塞,立即返回可能阻塞等待
适用场景简单的状态切换复杂的同步逻辑
代码复杂度简单相对复杂

优缺点分析

优点 ✅

  1. 轻量级:不需要创建锁对象
  2. 非阻塞:不会让线程进入 WAITING 状态
  3. 简洁:一行代码完成检查和设置
  4. 高效:在低竞争场景下性能更好

缺点 ❌

  1. 需要手动管理状态:必须在 finally 中重置标志
  2. 容易忘记释放:如果忘记 set(false),后续所有请求都会被拒绝
  3. 不支持重入:同一个线程不能多次获取
  4. ABA 问题:虽然 Boolean 类型不存在这个问题

完整示例

import java.util.concurrent.atomic.AtomicBoolean;

public class SingleTaskRunner {
    private final AtomicBoolean isRunning = new AtomicBoolean(false);
    
    /**
     * 执行任务,如果已有任务在执行则跳过
     */
    public void run(Runnable task) {
        // CAS 尝试获取执行权
        if (!isRunning.compareAndSet(false, true)) {
            System.out.println("⚠️ 任务正在执行中,本次请求被跳过");
            return;
        }
        
        System.out.println("✅ 获取执行权,开始执行任务");
        
        try {
            task.run();
            System.out.println("✅ 任务执行成功");
        } catch (Exception e) {
            System.err.println("❌ 任务执行异常: " + e.getMessage());
        } finally {
            // ⚠️ 重要:必须释放执行权
            isRunning.set(false);
            System.out.println("🔓 释放执行权");
        }
    }
    
    // 测试
    public static void main(String[] args) throws InterruptedException {
        SingleTaskRunner runner = new SingleTaskRunner();
        
        // 第一次调用:成功执行
        runner.run(() -> {
            System.out.println("任务1 执行中...");
            try { Thread.sleep(2000); } catch (InterruptedException e) {}
            System.out.println("任务1 执行完毕");
        });
        
        // 立即第二次调用:会被跳过
        runner.run(() -> {
            System.out.println("任务2 执行中...");
        });
        
        // 等待2秒后第三次调用:可以执行
        Thread.sleep(2500);
        runner.run(() -> {
            System.out.println("任务3 执行中...");
        });
    }
}

输出:

✅ 获取执行权,开始执行任务
任务1 执行中...
⚠️ 任务正在执行中,本次请求被跳过
任务1 执行完毕
🔓 释放执行权
✅ 获取执行权,开始执行任务
任务3 执行中...
🔓 释放执行权

总结

  • CAS 是一种"乐观"的原子操作:先假设能成功,如果不成功就放弃
  • AtomicBoolean 提供了线程安全的布尔值操作
  • compareAndSet 是核心方法:如果当前值符合预期,就更新并返回 true
  • 适合用于简单的状态标记防重复执行场景
  • 记得在 finally 块中释放状态,避免死锁
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

倒流时光三十年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值