跳转至

原子类与CAS (Atomic Classes & CAS)

深入理解Atomic类、CAS(Compare-And-Swap)原理、ABA问题及解决方案

目录


1. 为什么需要原子类 (Why Atomic Classes?)

1.1 传统同步的问题

/**
 * 传统同步方式的问题
 * Problems with Traditional Synchronization
 */
public class TraditionalSync {
    private int count = 0;

    // 方式1:使用synchronized(性能较低)
    public synchronized void increment() {
        count++;
    }

    // 方式2:使用volatile(不保证原子性)
    private volatile int count2 = 0;
    public void increment2() {
        count2++; // ❌ 不是原子操作!
    }
}

问题: - synchronized:性能较低,需要加锁 - volatile:只保证可见性,不保证原子性

1.2 原子类的优势

**原子类(Atomic Classes)**基于CAS实现,提供无锁的线程安全操作:

  • 无锁 - 基于CAS,性能高
  • 原子性 - 保证操作的原子性
  • 高性能 - 比synchronized性能更好

1.3 在算力平台中的应用

在算力平台的结算系统中,需要原子操作保证线程安全:

/**
 * 算力平台中的原子类应用
 * Atomic Classes in Computing Platform
 */
public class PlatformAtomic {

    // 场景1:任务计数器(原子操作)
    private AtomicInteger taskCount = new AtomicInteger(0);

    public void incrementTaskCount() {
        taskCount.incrementAndGet(); // 原子递增
    }

    // 场景2:用户钱包余额(原子操作)
    private ConcurrentHashMap<Long, AtomicLong> walletMap = new ConcurrentHashMap<>();

    public boolean deduct(Long userId, Long amount) {
        AtomicLong balance = walletMap.computeIfAbsent(
            userId, k -> new AtomicLong(0)
        );

        // 原子更新:CAS操作
        return balance.updateAndGet(current -> {
            if (current >= amount) {
                return current - amount;
            }
            throw new InsufficientBalanceException();
        }) >= 0;
    }

    // 场景3:统计信息(原子操作)
    private AtomicLong totalRevenue = new AtomicLong(0);

    public void addRevenue(Long amount) {
        totalRevenue.addAndGet(amount); // 原子累加
    }
}

2. CAS原理 (CAS Principle)

2.1 什么是CAS?

**CAS(Compare-And-Swap,比较并交换)**是一种无锁算法:

CAS操作包含三个操作数:
- 内存位置(V)
- 预期原值(A)
- 新值(B)

如果内存位置V的值等于预期原值A,则将V的值更新为B,否则不做任何操作。

2.2 CAS操作流程

/**
 * CAS操作伪代码
 * CAS Operation Pseudocode
 */
public boolean compareAndSwap(int memoryValue, int expectedValue, int newValue) {
    if (memoryValue == expectedValue) {
        memoryValue = newValue;
        return true; // 更新成功
    }
    return false; // 更新失败
}

实际执行: 1. 读取内存值V 2. 比较V和预期值A 3. 如果相等,更新为B;否则重试

2.3 CAS的原子性保证

CAS操作在CPU层面是原子的,通过**CPU指令**实现:

  • x86架构CMPXCHG指令
  • ARM架构LDREX/STREX指令对

2.4 CAS的优缺点

优点: - 无锁 - 不需要加锁,性能高 - 原子性 - CPU指令保证原子性 - 无阻塞 - 不会导致线程阻塞

缺点: - ABA问题 - 值可能被其他线程修改后又改回原值 - 自旋开销 - 如果竞争激烈,可能长时间自旋 - 只能保证一个变量 - 不能保证多个变量的原子性


3. AtomicInteger详解 (AtomicInteger Details)

3.1 核心方法

import java.util.concurrent.atomic.AtomicInteger;

/**
 * AtomicInteger核心方法
 * AtomicInteger Core Methods
 */
public class AtomicIntegerDemo {
    private AtomicInteger count = new AtomicInteger(0);

    // 基本操作
    public void basicOperations() {
        // 获取值
        int value = count.get();

        // 设置值
        count.set(10);

        // 获取并设置
        int oldValue = count.getAndSet(20);

        // 比较并设置(CAS)
        boolean success = count.compareAndSet(20, 30);
    }

    // 递增递减操作
    public void incrementDecrement() {
        // 递增并返回新值
        int newValue = count.incrementAndGet();

        // 返回旧值并递增
        int oldValue = count.getAndIncrement();

        // 递减并返回新值
        int newValue2 = count.decrementAndGet();

        // 返回旧值并递减
        int oldValue2 = count.getAndDecrement();
    }

    // 加减操作
    public void addSubtract() {
        // 加指定值并返回新值
        int newValue = count.addAndGet(5);

        // 返回旧值并加指定值
        int oldValue = count.getAndAdd(5);
    }

    // 更新操作
    public void update() {
        // 原子更新:使用函数式接口
        count.updateAndGet(x -> x * 2);

        // 获取并更新
        int oldValue = count.getAndUpdate(x -> x + 10);
    }
}

3.2 源码分析

/**
 * AtomicInteger核心实现(简化版)
 * Core Implementation (Simplified)
 */
public class AtomicInteger {
    private volatile int value;

    // CAS操作:使用Unsafe类
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    // 递增操作
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }

    // getAndAddInt实现(自旋)
    public final int getAndAddInt(Object obj, long offset, int delta) {
        int v;
        do {
            v = getIntVolatile(obj, offset); // 获取当前值
        } while (!compareAndSwapInt(obj, offset, v, v + delta)); // CAS直到成功
        return v;
    }
}

关键点: - 使用volatile保证可见性 - 使用Unsafe.compareAndSwapInt()实现CAS - 自旋直到CAS成功


4. 其他原子类 (Other Atomic Classes)

4.1 AtomicLong

import java.util.concurrent.atomic.AtomicLong;

/**
 * AtomicLong使用示例
 * AtomicLong Usage Example
 */
public class AtomicLongDemo {
    private AtomicLong counter = new AtomicLong(0);

    public void increment() {
        counter.incrementAndGet();
    }

    public long get() {
        return counter.get();
    }
}

4.2 AtomicBoolean

import java.util.concurrent.atomic.AtomicBoolean;

/**
 * AtomicBoolean使用示例
 * AtomicBoolean Usage Example
 */
public class AtomicBooleanDemo {
    private AtomicBoolean flag = new AtomicBoolean(false);

    public boolean trySet() {
        // 只有当前值为false时才设置为true
        return flag.compareAndSet(false, true);
    }
}

4.3 AtomicReference

import java.util.concurrent.atomic.AtomicReference;

/**
 * AtomicReference使用示例
 * AtomicReference Usage Example
 */
public class AtomicReferenceDemo {
    private AtomicReference<String> ref = new AtomicReference<>("初始值");

    public void update() {
        // 原子更新引用
        ref.compareAndSet("初始值", "新值");

        // 使用函数式更新
        ref.updateAndGet(s -> s + "追加");
    }
}

4.4 原子数组类

import java.util.concurrent.atomic.AtomicIntegerArray;

/**
 * AtomicIntegerArray使用示例
 * AtomicIntegerArray Usage Example
 */
public class AtomicIntegerArrayDemo {
    private AtomicIntegerArray array = new AtomicIntegerArray(10);

    public void update(int index, int value) {
        array.set(index, value);
        array.compareAndSet(index, value, value + 1);
    }
}

4.5 原子更新字段类

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

/**
 * AtomicIntegerFieldUpdater使用示例
 * AtomicIntegerFieldUpdater Usage Example
 */
public class AtomicIntegerFieldUpdaterDemo {
    static class Counter {
        volatile int count = 0; // 必须是volatile
    }

    private static final AtomicIntegerFieldUpdater<Counter> updater =
        AtomicIntegerFieldUpdater.newUpdater(Counter.class, "count");

    public void update(Counter counter) {
        updater.incrementAndGet(counter);
    }
}

5. ABA问题 (ABA Problem)

5.1 什么是ABA问题?

ABA问题:CAS操作时,值从A变为B,再变回A,CAS认为值没有变化,但实际上已经被修改过。

5.2 ABA问题示例

/**
 * ABA问题示例
 * ABA Problem Example
 */
public class ABAProblem {
    private AtomicReference<String> ref = new AtomicReference<>("A");

    public void demonstrateABA() {
        // 线程1:A -> B -> A
        new Thread(() -> {
            ref.compareAndSet("A", "B");
            ref.compareAndSet("B", "A");
        }).start();

        // 线程2:检查值是否为A,如果是则更新为C
        new Thread(() -> {
            try {
                Thread.sleep(100); // 等待线程1完成
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 此时值已经是A(但中间被修改过),CAS成功
            boolean success = ref.compareAndSet("A", "C");
            System.out.println("更新成功: " + success); // true
        }).start();
    }
}

5.3 解决方案:AtomicStampedReference

import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * 使用版本号解决ABA问题
 * Solve ABA Problem with Version Number
 */
public class ABAProblemSolution {
    private AtomicStampedReference<String> ref = 
        new AtomicStampedReference<>("A", 1); // 初始值A,版本号1

    public void solveABA() {
        // 线程1:A(1) -> B(2) -> A(3)
        new Thread(() -> {
            int[] stampHolder = new int[1];
            String value = ref.get(stampHolder);
            ref.compareAndSet(value, "B", stampHolder[0], stampHolder[0] + 1);

            value = ref.get(stampHolder);
            ref.compareAndSet(value, "A", stampHolder[0], stampHolder[0] + 1);
        }).start();

        // 线程2:检查值和版本号
        new Thread(() -> {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            int[] stampHolder = new int[1];
            String value = ref.get(stampHolder);
            // 版本号不匹配,更新失败
            boolean success = ref.compareAndSet("A", "C", 1, 2);
            System.out.println("更新成功: " + success); // false
        }).start();
    }
}

5.4 解决方案:AtomicMarkableReference

import java.util.concurrent.atomic.AtomicMarkableReference;

/**
 * 使用标记解决ABA问题
 * Solve ABA Problem with Mark
 */
public class ABAProblemSolution2 {
    private AtomicMarkableReference<String> ref = 
        new AtomicMarkableReference<>("A", false);

    public void solveABA() {
        // 使用boolean标记代替版本号
        boolean[] markHolder = new boolean[1];
        String value = ref.get(markHolder);
        ref.compareAndSet(value, "B", false, true);
    }
}

6. LongAdder vs AtomicLong (LongAdder vs AtomicLong)

6.1 性能对比

在高并发场景下,LongAdder性能优于AtomicLong

特性 AtomicLong LongAdder
实现 单个volatile变量 分段累加(Cell数组)
低并发 性能相当 性能相当
高并发 性能下降(CAS竞争) 性能更好(分散竞争)
内存 占用少 占用多(Cell数组)
精度 精确 最终一致(读取时累加)

6.2 LongAdder原理

import java.util.concurrent.atomic.LongAdder;

/**
 * LongAdder原理:分段累加
 * LongAdder Principle: Segmented Accumulation
 */
public class LongAdderDemo {
    private LongAdder adder = new LongAdder();

    public void increment() {
        adder.increment(); // 分散到不同的Cell
    }

    public long sum() {
        return adder.sum(); // 累加所有Cell的值
    }
}

原理: - 内部维护一个Cell数组 - 每个线程累加到不同的Cell - 读取时累加所有Cell的值 - 减少CAS竞争,提高性能

6.3 使用建议

/**
 * 使用建议
 * Usage Recommendations
 */
public class AtomicChoice {

    // 低并发场景:使用AtomicLong
    private AtomicLong lowConcurrencyCounter = new AtomicLong(0);

    // 高并发场景:使用LongAdder
    private LongAdder highConcurrencyCounter = new LongAdder();

    // 需要精确值:使用AtomicLong
    private AtomicLong preciseCounter = new AtomicLong(0);

    // 可以容忍最终一致:使用LongAdder
    private LongAdder eventualConsistentCounter = new LongAdder();
}

7. 最佳实践 (Best Practices)

7.1 选择合适的原子类

// 简单计数器:AtomicInteger
AtomicInteger counter = new AtomicInteger(0);

// 高并发计数器:LongAdder
LongAdder highConcurrencyCounter = new LongAdder();

// 引用类型:AtomicReference
AtomicReference<String> ref = new AtomicReference<>();

// 需要版本号:AtomicStampedReference
AtomicStampedReference<String> stampedRef = 
    new AtomicStampedReference<>("value", 1);

7.2 避免ABA问题

// ✅ 使用版本号
AtomicStampedReference<String> ref = 
    new AtomicStampedReference<>("A", 1);

// ✅ 使用标记
AtomicMarkableReference<String> ref = 
    new AtomicMarkableReference<>("A", false);

7.3 合理使用LongAdder

// 高并发累加:使用LongAdder
LongAdder totalRevenue = new LongAdder();
for (int i = 0; i < 1000; i++) {
    executorService.submit(() -> {
        totalRevenue.add(100);
    });
}
long sum = totalRevenue.sum(); // 最终累加值

8. 面试高频问题 (Interview Questions)

Q1: CAS的原理是什么?

答案: Compare-And-Swap,比较内存值和预期值,如果相等则更新为新值,否则重试。通过CPU指令保证原子性。

Q2: CAS的优缺点?

答案: - 优点:无锁、高性能、无阻塞 - 缺点:ABA问题、自旋开销、只能保证一个变量

Q3: 什么是ABA问题?如何解决?

答案: 值从A变为B再变回A,CAS认为值没变化。使用AtomicStampedReference(版本号)或AtomicMarkableReference(标记)解决。

Q4: LongAdder和AtomicLong的区别?

答案: LongAdder使用分段累加,高并发性能更好;AtomicLong使用单个变量,低并发性能相当。

Q5: 什么时候使用原子类?

答案: 简单的原子操作(如计数器),需要高性能的场景,可以替代synchronized的场景。


📖 扩展阅读


返回: 07-Java并发编程
上一章: 07-06 - 并发集合
下一章: 07-08 - 并发设计模式 →