Skip to content

基础原子类

100 个线程同时对同一个变量执行 i++,最终结果一定是 100 吗?

如果你用 int i = 0,答案大概率是——因为 i++ 不是原子操作。

但如果用 AtomicInteger i = new AtomicInteger(0),答案是肯定的。

这就是原子类的作用。

原子类家族

Java 并发包 java.util.concurrent.atomic 提供了完整的原子类家族:

java.util.concurrent.atomic
├── 基础类型
│   ├── AtomicInteger       int 类型的原子操作
│   ├── AtomicLong          long 类型的原子操作
│   └── AtomicBoolean       boolean 类型的原子操作

├── 引用类型
│   ├── AtomicReference<V>          普通引用
│   ├── AtomicStampedReference<V>   带版本号的引用(解决 ABA)
│   └── AtomicMarkableReference<V>  带标记的引用

├── 数组类型
│   ├── AtomicIntegerArray
│   ├── AtomicLongArray
│   └── AtomicReferenceArray<V>

└── 字段更新器
    ├── AtomicIntegerFieldUpdater<T>
    ├── AtomicLongFieldUpdater<T>
    └── AtomicReferenceFieldUpdater<T,V>

常用方法一览

方法说明
get()获取当前值
set(value)设置值
lazySet(value)延迟设置值(其他线程可能暂时读到旧值)
getAndSet(value)原子地设置新值,返回旧值
compareAndSet(expected, update)CAS 操作
incrementAndGet()递增并返回新值
getAndIncrement()返回旧值后再递增
decrementAndGet()递减并返回新值
getAndDecrement()返回旧值后再递减
addAndGet(delta)增加 delta 并返回新值
getAndAdd(delta)返回旧值后再增加

注意带 "getAnd" 前缀的方法都是返回旧值,不带前缀的返回新值

代码演示

AtomicInteger 基础操作

java
public class AtomicIntegerDemo {

    private static final AtomicInteger counter = new AtomicInteger(0);

    public static void main(String[] args) {
        System.out.println("初始值: " + counter.get());  // 0

        counter.set(10);
        System.out.println("set(10): " + counter.get());  // 10

        counter.incrementAndGet();
        System.out.println("incrementAndGet(): " + counter.get());  // 11

        counter.decrementAndGet();
        System.out.println("decrementAndGet(): " + counter.get());  // 10

        counter.addAndGet(5);
        System.out.println("addAndGet(5): " + counter.get());  // 15

        int old = counter.getAndAdd(3);
        System.out.println("getAndAdd(3) 返回旧值: " + old);  // 15
        System.out.println("当前值: " + counter.get());  // 18
    }
}

CAS 操作

java
public class AtomicCASDemo {

    private static final AtomicInteger value = new AtomicInteger(0);

    public static void main(String[] args) {
        // compareAndSet 成功情况
        boolean success1 = value.compareAndSet(0, 10);
        System.out.println("CAS(0->10): " + success1);  // true
        System.out.println("当前值: " + value.get());    // 10

        // compareAndSet 失败情况
        boolean success2 = value.compareAndSet(0, 20);
        System.out.println("CAS(0->20): " + success2);  // false
        System.out.println("当前值: " + value.get());    // 仍然是 10
    }
}

无锁计数器

java
public class AtomicCounter {

    private final AtomicInteger count = new AtomicInteger(0);

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

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

    public static void main(String[] args) throws InterruptedException {
        AtomicCounter counter = new AtomicCounter();
        int threadCount = 100;

        for (int i = 0; i &lt; threadCount; i++) {
            new Thread(() -> {
                for (int j = 0; j &lt; 1000; j++) {
                    counter.increment();
                }
            }).start();
        }

        Thread.sleep(2000);
        System.out.println("计数结果: " + counter.get());  // 应该是 100000
    }
}

AtomicLong 计时器

java
public class AtomicLongTimer {

    private final AtomicLong startTime = new AtomicLong(0);
    private final AtomicLong endTime = new AtomicLong(0);

    public void start() {
        startTime.set(System.currentTimeMillis());
    }

    public void end() {
        endTime.set(System.currentTimeMillis());
    }

    public long duration() {
        return endTime.get() - startTime.get();
    }

    public static void main(String[] args) {
        AtomicLongTimer timer = new AtomicLongTimer();

        timer.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        timer.end();

        System.out.println("耗时: " + timer.duration() + "ms");
    }
}

AtomicBoolean 单次初始化

java
public class AtomicBooleanDemo {

    private static final AtomicBoolean initialized = new AtomicBoolean(false);

    public static void initialize() {
        // 只有一个线程能成功初始化
        if (initialized.compareAndSet(false, true)) {
            System.out.println("初始化完成");
        } else {
            System.out.println("已经初始化过了");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i &lt; 5; i++) {
            new Thread(() -> initialize(), "Thread-" + i).start();
        }

        Thread.sleep(1000);
    }
}

Lambda 更新函数

JDK 8+ 提供了更简洁的更新方式:

java
public class AtomicUpdateFunctions {

    public static void main(String[] args) {
        AtomicInteger ai = new AtomicInteger(10);

        // updateAndGet: 更新后获取
        int result1 = ai.updateAndGet(x -> x * 2);
        System.out.println("updateAndGet(x*2): " + result1);  // 20

        // getAndUpdate: 获取后再更新
        int result2 = ai.getAndUpdate(x -> x + 5);
        System.out.println("getAndUpdate(x+5) 返回: " + result2);  // 20
        System.out.println("当前值: " + ai.get());  // 25

        // accumulateAndGet: 累加
        int result3 = ai.accumulateAndGet(3, Integer::sum);
        System.out.println("accumulateAndGet(3, sum): " + result3);  // 28

        // getAndAccumulate: 获取后再累加
        int result4 = ai.getAndAccumulate(2, Integer::sum);
        System.out.println("getAndAccumulate(2, sum) 返回: " + result4);  // 28
        System.out.println("当前值: " + ai.get());  // 30
    }
}

注意事项

  1. 性能考虑:CAS 在高竞争下可能多次重试,极端情况下不如 synchronized
  2. ABA 问题:使用 AtomicStampedReference 解决(后面会详细讲)
  3. 复合操作map.get(k) + 1 这种仍不是原子的,需要用循环 CAS
  4. long/double:在 32 位 JVM 中,AtomicLong 使用 CAS 保证 64 位操作的原子性

选择指南

场景推荐
简单计数AtomicInteger
需要 long 范围AtomicLong
布尔状态开关AtomicBoolean
引用类型AtomicReference
高并发累加LongAdder(见专门章节)

基于 VitePress 构建