淘宝Java面试被问:Atomic原子类的实现原理

Atomic原子类的实现原理

一、核心思想:无锁并发

Atomic原子类的核心是 CAS(Compare-And-Swap) 操作,它解决了传统synchronized锁的性能瓶颈问题:

java

复制代码
// 传统加锁方式 vs CAS方式
public class LockVsCAS {
    private int value;
    
    // 方式一:synchronized(悲观锁)
    public synchronized void increment() {
        value++;  // ① 获取锁 ② 执行 ③ 释放锁
    }
    
    // 方式二:CAS(乐观锁)
    public void incrementAtomic() {
        int oldValue, newValue;
        do {
            oldValue = value;          // 读取当前值
            newValue = oldValue + 1;   // 计算新值
        } while (!compareAndSwap(oldValue, newValue)); // 尝试更新
        // 如果期间value被其他线程修改,则重试
    }
}

二、核心机制:CAS(Compare-And-Swap)

1. CAS的底层原理

CAS是CPU级别的原子指令,在Java中通过Unsafe类的本地方法实现:

java

复制代码
// 伪代码展示CAS原理
public class CASDemo {
    // CAS核心操作(伪代码)
    public boolean compareAndSwap(int expectedValue, int newValue) {
        // 原子操作:比较并交换
        if (currentValue == expectedValue) {  // 检查值是否被其他线程修改
            currentValue = newValue;          // 未被修改,则更新
            return true;
        }
        return false;  // 被修改了,返回失败
    }
    
    // Java中的实际实现(通过Unsafe)
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private volatile int value;
    private static final long valueOffset;  // value字段的内存偏移量
    
    static {
        try {
            // 获取value字段在对象内存布局中的偏移地址
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }
    
    public final boolean compareAndSet(int expect, int update) {
        // 调用CPU的CAS指令
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
}

2. Unsafe 类的作用

java

复制代码
// Unsafe提供了一系列硬件级别的原子操作
public class UnsafeCAS {
    // 1. 对象字段操作
    native long objectFieldOffset(Field f);  // 获取字段偏移量
    native int getIntVolatile(Object o, long offset);  // 原子读
    native boolean compareAndSwapInt(      // CAS操作
        Object o, long offset, 
        int expected, int x
    );
    
    // 2. 数组元素操作
    native int arrayBaseOffset(Class arrayClass);  // 数组基地址
    native int arrayIndexScale(Class arrayClass);  // 元素大小
    native int getAndSetInt(Object o, long offset, int newValue);
    
    // 3. 内存屏障(JDK 8+)
    native void loadFence();   // 读屏障
    native void storeFence();  // 写屏障
    native void fullFence();   // 全屏障
}

三、Atomic原子类家族

1. 基础原子类

java

复制代码
// AtomicInteger 源码分析
public class AtomicInteger extends Number implements java.io.Serializable {
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;
    private volatile int value;  // 关键:volatile保证可见性
    
    // 核心方法:原子自增
    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }
    
    // Unsafe中的实现
    public final int getAndAddInt(Object o, long offset, int delta) {
        int v;
        do {
            v = getIntVolatile(o, offset);  // 原子读取当前值
        } while (!compareAndSwapInt(o, offset, v, v + delta)); // CAS重试
        return v;
    }
    
    // 其他常用方法
    public final int incrementAndGet() {}     // ++i
    public final int getAndDecrement() {}     // i--
    public final boolean compareAndSet(int expect, int update) {} // CAS
    public final int getAndUpdate(IntUnaryOperator updateFunction) {}
}

2. 数组原子类

java

复制代码
// AtomicIntegerArray - 支持对数组元素的原子操作
public class AtomicIntegerArrayDemo {
    private final AtomicIntegerArray array = new AtomicIntegerArray(10);
    
    public void demo() {
        // 原子更新数组元素
        array.compareAndSet(0, 0, 100);  // 如果array[0]==0,则设为100
        
        // 底层实现:计算元素的内存地址
        // 地址 = 数组基地址 + 索引 × 元素大小
        long rawIndex = arrayOffset + (long) index * scale;
    }
}

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】

3. 引用类型原子类

java

复制代码
// AtomicReference - 原子更新对象引用
public class AtomicReferenceDemo {
    static class User {
        String name;
        int age;
    }
    
    private AtomicReference<User> ref = new AtomicReference<>();
    
    public void updateUser() {
        User oldUser, newUser;
        do {
            oldUser = ref.get();
            newUser = new User("Tom", oldUser.age + 1);
        } while (!ref.compareAndSet(oldUser, newUser));
    }
    
    // AtomicStampedReference - 解决ABA问题
    private AtomicStampedReference<Integer> stampedRef = 
        new AtomicStampedReference<>(100, 0);
    
    public void solveABA() {
        int[] stampHolder = new int[1];
        int currentStamp, newStamp;
        Integer currentRef, newRef;
        
        do {
            currentRef = stampedRef.get(stampHolder);  // 同时获取引用和版本号
            currentStamp = stampHolder[0];
            newRef = currentRef + 10;
            newStamp = currentStamp + 1;  // 每次更新增加版本号
        } while (!stampedRef.compareAndSet(
            currentRef, newRef, currentStamp, newStamp
        ));
    }
}

4. 字段更新器

java

复制代码
// 原子更新对象的某个字段(无需修改类定义)
public class FieldUpdaterDemo {
    static class Counter {
        volatile int count;  // 必须是volatile
    }
    
    public void demo() {
        Counter counter = new Counter();
        AtomicIntegerFieldUpdater<Counter> updater = 
            AtomicIntegerFieldUpdater.newUpdater(Counter.class, "count");
        
        updater.incrementAndGet(counter);  // 原子增加counter.count
        
        // 应用场景:大量对象需要原子计数,避免为每个对象创建AtomicInteger
    }
}

四、ABA问题及解决方案

1. ABA问题示例

java

复制代码
public class ABAProblem {
    private static AtomicInteger atomic = new AtomicInteger(100);
    
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            // 线程1:A->B
            boolean success = atomic.compareAndSet(100, 101);
            System.out.println("t1: 100->101 " + success);
            success = atomic.compareAndSet(101, 100);
            System.out.println("t1: 101->100 " + success);
        });
        
        Thread t2 = new Thread(() -> {
            try {
                Thread.sleep(100);  // 确保t1先执行完ABA操作
            } catch (InterruptedException e) {}
            
            // 线程2:期望看到100,就改为200
            boolean success = atomic.compareAndSet(100, 200);
            System.out.println("t2: 100->200 " + success); // ✅ 成功!
            // 问题:虽然成功了,但中间发生过100->101->100的变化
        });
        
        t1.start(); t2.start();
        t1.join(); t2.join();
    }
}

2. 解决方案:AtomicStampedReference

java

复制代码
public class ABASolution {
    private static AtomicStampedReference<Integer> stampedRef = 
        new AtomicStampedReference<>(100, 0);
    
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            int stamp = stampedRef.getStamp();
            // A->B,版本号+1
            stampedRef.compareAndSet(100, 101, stamp, stamp + 1);
            stamp = stampedRef.getStamp();
            // B->A,版本号再+1
            stampedRef.compareAndSet(101, 100, stamp, stamp + 1);
        });
        
        Thread t2 = new Thread(() -> {
            try { Thread.sleep(100); } catch (InterruptedException e) {}
            
            int[] stampHolder = new int[1];
            int currentValue = stampedRef.get(stampHolder);
            int currentStamp = stampHolder[0];
            
            // 虽然值还是100,但版本号变了,所以CAS失败!
            boolean success = stampedRef.compareAndSet(
                100, 200, currentStamp, currentStamp + 1
            );
            System.out.println("t2 CAS结果: " + success); // ❌ 失败!
        });
        
        t1.start(); t2.start();
        t1.join(); t2.join();
    }
}

五、JDK 8+ 的增强

1. 更高效的累积器(LongAdder/DoubleAdder)

java

复制代码
// LongAdder - 高并发下性能远超AtomicLong
public class LongAdderDemo {
    private final LongAdder adder = new LongAdder();
    
    public void highConcurrencyScenario() {
        // 原理:分段累加,减少竞争
        // base + Cell[0] + Cell[1] + ... + Cell[n]
        
        adder.increment();  // 快速累加
        
        // 适用场景:大量并发写,偶尔读(如计数器、监控统计)
        long sum = adder.sum();  // 读取时需要合并所有分段
    }
    
    // 内部结构
    static class LongAdderInternal {
        transient volatile Cell[] cells;  // 分段数组
        transient volatile long base;     // 基础值
        transient volatile int cellsBusy; // 扩容锁
        
        // Cell类使用@Contended避免伪共享
        @sun.misc.Contended  
        static final class Cell {
            volatile long value;
            Cell(long x) { value = x; }
        }
    }
}

2. 性能对比

java

复制代码
// AtomicLong vs LongAdder 性能测试
public class PerformanceComparison {
    public static void main(String[] args) {
        // 场景:100个线程,每个累加100万次
        
        // AtomicLong: ≈ 4500ms
        // 原理:所有线程竞争同一个变量,大量CAS失败重试
        
        // LongAdder: ≈ 800ms
        // 原理:线程哈希到不同Cell,减少竞争,最后合并
    }
}

3. 新的原子操作方法

java

复制代码
public class JDK8NewFeatures {
    private AtomicInteger atomic = new AtomicInteger(0);
    
    public void newMethods() {
        // 1. 函数式更新
        atomic.getAndUpdate(x -> x * 2);      // i = i * 2
        atomic.updateAndGet(x -> x + 10);     // i = i + 10
        
        // 2. 累积操作
        atomic.getAndAccumulate(5, (x, y) -> x + y);  // i = i + 5
        atomic.accumulateAndGet(3, (x, y) -> x * y);  // i = i * 3
        
        // 3. 懒设置(不保证立即对其他线程可见)
        atomic.lazySet(100);  // 性能更高,适合后续不用该值的场景
    }
}

六、内存模型与可见性

1. volatile 的关键作用

java

复制代码
public class AtomicMemoryModel {
    // Atomic类中value字段必须用volatile修饰
    private volatile int value;
    
    // volatile保证:
    // 1. 可见性:一个线程的修改对其他线程立即可见
    // 2. 禁止指令重排序:防止编译器/CPU优化破坏原子性
    // 3. happens-before:对volatile域的写入happens-before后续读取
}

2. 内存屏障实现

java

复制代码
// Unsafe中的内存屏障操作(JDK 8+)
public class MemoryBarrierDemo {
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    
    public void barrierOperations() {
        // 1. 读屏障:确保屏障前的读操作先于屏障后的读操作完成
        unsafe.loadFence();
        
        // 2. 写屏障:确保屏障前的写操作先于屏障后的写操作完成
        unsafe.storeFence();
        
        // 3. 全屏障:同时包含读屏障和写屏障
        unsafe.fullFence();
        
        // CAS操作内部会自动插入内存屏障
    }
}

七、CAS的局限性及解决方案

1. CAS的三大问题

java

复制代码
public class CASLimitations {
    // 1. ABA问题(已解决:AtomicStampedReference)
    // 2. 循环时间长开销大
    public void spinTooLong() {
        // 高竞争下,大量线程CAS失败,不断重试
        while (!cas()) {
            // 长时间空转消耗CPU
        }
    }
    
    // 3. 只能保证一个共享变量的原子操作
    public void multipleVariables() {
        // ❌ 无法原子地同时更新两个变量
        // atomic1.compareAndSet(...) && atomic2.compareAndSet(...)
        // 这不是原子操作!
        
        // ✅ 解决方案:
        // a. 使用AtomicReference包裹多个变量
        // b. 使用锁
    }
}

2. 自旋优化策略

java

复制代码
// 自适应自旋锁(JVM实现)
public class AdaptiveSpinning {
    // JVM根据历史成功率动态调整自旋次数
    // 1. 如果上次CAS成功,则增加自旋次数
    // 2. 如果上次CAS失败,则减少自旋次数
    // 3. 可能直接挂起线程,避免CPU空转
}

八、实战应用示例

1. 高性能计数器

java

复制代码
// 根据并发度选择不同的原子类
public class SmartCounter {
    private final boolean highContention;
    private final Object counter;
    
    public SmartCounter(boolean highContention) {
        this.highContention = highContention;
        this.counter = highContention ? new LongAdder() : new AtomicLong();
    }
    
    public void increment() {
        if (highContention) {
            ((LongAdder) counter).increment();
        } else {
            ((AtomicLong) counter).incrementAndGet();
        }
    }
    
    public long get() {
        return highContention ? 
            ((LongAdder) counter).sum() : 
            ((AtomicLong) counter).get();
    }
}

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】

2. 无锁栈实现

java

复制代码
// 使用AtomicReference实现无锁栈
public class LockFreeStack<T> {
    private static class Node<T> {
        final T value;
        Node<T> next;
        Node(T value) { this.value = value; }
    }
    
    private final AtomicReference<Node<T>> top = new AtomicReference<>();
    
    public void push(T value) {
        Node<T> newHead = new Node<>(value);
        Node<T> oldHead;
        do {
            oldHead = top.get();
            newHead.next = oldHead;
        } while (!top.compareAndSet(oldHead, newHead));
    }
    
    public T pop() {
        Node<T> oldHead, newHead;
        do {
            oldHead = top.get();
            if (oldHead == null) return null;
            newHead = oldHead.next;
        } while (!top.compareAndSet(oldHead, newHead));
        return oldHead.value;
    }
}

九、总结与最佳实践

1. 原子类选择指南

场景 推荐类 原因
低竞争计数器 AtomicInteger/Long 简单直接
高竞争计数器 LongAdder/DoubleAdder 分段减少竞争
对象引用更新 AtomicReference 原子更新引用
需要版本控制 AtomicStampedReference 解决ABA问题
批量对象字段更新 Atomic*FieldUpdater 节省内存

2. 使用注意事项

java

复制代码
public class BestPractices {
    // ✅ 正确做法
    public void goodPractice() {
        // 1. 使用static final修饰
        private static final AtomicInteger COUNTER = new AtomicInteger();
        
        // 2. 考虑使用LongAdder替代AtomicLong(高并发写)
        
        // 3. 避免过度使用:非竞争场景用普通变量
        
        // 4. 复杂复合操作仍需锁
        if (atomic.get() > threshold) {
            synchronized (this) {  // 需要原子检查+操作
                if (atomic.get() > threshold) {
                    doSomething();
                    atomic.decrementAndGet();
                }
            }
        }
    }
    
    // ❌ 错误做法
    public void badPractice() {
        // 1. 频繁创建Atomic对象(应在类级别共享)
        
        // 2. 用Atomic实现复杂业务逻辑(应用锁)
        
        // 3. 忽略ABA问题(涉及资金、状态等关键数据)
    }
}

3. 性能优化建议

  1. 减少竞争 :使用LongAdder代替AtomicLong

  2. 避免伪共享 :使用@Contended注解(JDK 8+)

  3. 合理使用lazySet:不需要立即可见性的场景

  4. 监控CAS成功率:高失败率说明竞争激烈,需优化

核心原理总结 :Atomic原子类通过 CAS + volatile + Unsafe 实现了高效的无锁并发,是现代并发编程的基石。理解其原理有助于在合适场景选择合适工具,构建高性能并发系统。

相关推荐
expect7g4 小时前
Paimon源码解读 -- Compaction-9.SortMergeReaderWithLoserTree
大数据·后端·flink
laocooon5238578864 小时前
C++中的安全指针(智能指针)
开发语言·c++
咸鱼加辣4 小时前
【python面试题】LRUCache
开发语言·python
LitchiCheng4 小时前
WSL2 中 pynput 无法捕获按键输入?
开发语言·python
中年程序员一枚4 小时前
Python 中处理视频添加 / 替换音频
开发语言·python·音视频
yuuki2332334 小时前
【C++】模板初阶
java·开发语言·c++
qq_12498707534 小时前
基于Spring Boot的社区医院管理系统的设计与实现(源码+论文+部署+安装)
java·数据库·人工智能·spring boot·毕业设计
爱吃大芒果4 小时前
Flutter 路由进阶:命名路由、动态路由与路由守卫实现
开发语言·javascript·flutter·华为·ecmascript
程序员爱钓鱼4 小时前
BlackHole 2ch:macOS无杂音录屏与系统音频采集完整技术指南
前端·后端·设计模式