分布式、高并发-Day04

以下是 Day 4 详细学习内容(CAS 与原子操作实战,30 分钟完整计划),包含原理解析、分步代码实战和性能对比:

📖 今日学习目标

  • 掌握 CAS(Compare-And-Swap)无锁算法的核心原理
  • 学会使用AtomicInteger实现线程安全的计数器
  • 理解 ABA 问题及解决方案(AtomicStampedReference)

⏰ 时间分配

时间段任务详细内容
0-10 分钟理论:CAS 原理与原子类1. CAS 三要素:内存值 (V)、旧值 (A)、新值 (B)2. AtomicInteger底层实现3. ABA 问题演示与解决
10-25 分钟实战:无锁计数器对比实验1. 用 CAS 实现线程安全计数器2. 用synchronized实现同步计数器3. 对比两种方式的性能与资源占用
25-30 分钟总结与扩展1. 记录 CAS 的优势与适用场景2. 思考:为什么 CAS 比synchronized更高效?3. 扩展:如何用 CAS 实现自定义原子操作?

🔍 理论详解:CAS 核心概念

  1. CAS 是什么?
  • 全称:Compare-And-Swap(比较并交换)
  • 核心逻辑:
    • 仅当内存中的值 V 等于预期旧值 A 时,才将其更新为新值 B,否则不做任何操作。
// CAS伪代码
boolean cas(V, A, B) {
    if (V == A) {
        V = B; // 原子操作,由CPU硬件保证
        return true;
    }
    return false;
}

**优势:**无锁编程,避免线程阻塞(上下文切换损耗),适合竞争不激烈的场景。
2. ABA 问题与解决方案

  • 问题场景:
    • 线程 1 读取值为 A,线程 2 将其改为 B,再改回 A,线程 1 执行 CAS 时发现值还是 A,误以为未被修改。
  • 解决方案:
    • 使用AtomicStampedReference,在值之外附加一个版本号(时间戳),每次修改版本号递增:
AtomicStampedReference<Integer> ref = new AtomicStampedReference<>(100, 0);
int stamp = ref.getStamp(); // 获取初始版本号
ref.compareAndSet(100, 200, stamp, stamp + 1); // 版本号必须匹配才成功
  1. 原子类家族
类名作用底层实现
AtomicInteger整数原子操作Unsafe 类的 CAS 方法
AtomicLong长整型原子操作同上
AtomicReference对象引用原子操作CAS 操作对象地址

💻 实战步骤:无锁计数器对比实验

  • 实验 1:用 CAS 实现计数器(AtomicInteger)
import java.util.concurrent.atomic.AtomicInteger;

public class CASCounter {
    private AtomicInteger count = new AtomicInteger(0);

    public void increment() {
        count.incrementAndGet(); // 底层调用CAS
    }

    public int getCount() {
        return count.get();
    }

    public static void main(String[] args) throws InterruptedException {
        CASCounter counter = new CASCounter();
        Thread[] threads = new Thread[10];
        for (int i = 0; i < 10; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 10000; j++) {
                    counter.increment();
                }
            });
        }

        for (Thread t : threads) {
            t.start();
        }
        for (Thread t : threads) {
            t.join();
        }
        System.out.println("CAS计数器结果:" + counter.getCount()); // 预期:100000
    }
}
  • 实验 2:用synchronized实现同步计数器
public class SynchronizedCounter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }

    public static void main(String[] args) throws InterruptedException {
        SynchronizedCounter counter = new SynchronizedCounter();
        Thread[] threads = new Thread[10];
        for (int i = 0; i < 10; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 10000; j++) {
                    counter.increment();
                }
            });
        }

        for (Thread t : threads) {
            t.start();
        }
        for (Thread t : threads) {
            t.join();
        }
        System.out.println("Synchronized计数器结果:" + counter.getCount()); // 预期:100000
    }
}
  1. 性能对比(选做,5 分钟)
  • 添加计时代码:
long start = System.currentTimeMillis();
// 执行计数逻辑
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start) + "ms");
  • 预期结果:
  • CAS 版本:耗时更短(无锁,减少上下文切换)
  • Synchronized 版本:耗时较长(锁竞争导致线程阻塞)

📝 今日总结与扩展

  1. 核心知识点速记
特性CAS(AtomicInteger)synchronized
锁机制无锁(乐观锁)有锁(悲观锁)
适用场景低竞争场景(如计数、状态标记)高竞争场景(如临界资源保护)
性能高(减少线程阻塞)低(可能触发锁升级)
ABA 问题需AtomicStampedReference解决无(锁保证原子性)
  1. 扩展思考(5 分钟)
  • 问题 1:CAS 操作会导致 “忙等待” 吗?
    答案:是的,CAS 失败时会循环重试(自旋),如果长时间不成功,反而比synchronized更耗 CPU。
  • 问题 2:生产环境如何选择 CAS 还是锁?
    提示:
    读多写少、竞争低:优先用 CAS(如计数器、配置开关)
    写多读少、竞争高:优先用synchronized或ReentrantLock

🔧 工具与环境准备

代码要求:直接复制两个 Java 文件,无需额外依赖(JDK 1.8+)
运行验证:
确保两个程序输出均为 100000(线程安全验证通过)
观察控制台是否有异常(如 ABA 问题未解决时可能出现数据错误)

✅ 今日任务 checklist

✅ 理解 CAS 的 “比较 - 交换” 核心逻辑
✅ 成功运行两个计数器程序,验证线程安全性
✅ 记录 1 个 CAS 应用场景(如:分布式系统中的版本号控制)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值