跳转至

原子类与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 - 并发设计模式 →