淘宝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 实现了高效的无锁并发,是现代并发编程的基石。理解其原理有助于在合适场景选择合适工具,构建高性能并发系统。

相关推荐
@大迁世界2 分钟前
TypeScript 的本质并非类型,而是信任
开发语言·前端·javascript·typescript·ecmascript
xiaolyuh1234 分钟前
Arthas修改类(如加日志)的实现原理
java
栗子叶8 分钟前
Java对象创建的过程
java·开发语言·jvm
勇哥java实战分享13 分钟前
短信平台 Pro 版本 ,比开源版本更强大
后端
Amumu1213816 分钟前
React面向组件编程
开发语言·前端·javascript
学历真的很重要17 分钟前
LangChain V1.0 Context Engineering(上下文工程)详细指南
人工智能·后端·学习·语言模型·面试·职场和发展·langchain
有一个好名字17 分钟前
力扣-从字符串中移除星号
java·算法·leetcode
IT=>小脑虎17 分钟前
Python零基础衔接进阶知识点【详解版】
开发语言·人工智能·python
wjs202420 分钟前
C 标准库 - `<float.h>》详解
开发语言
计算机毕设VX:Fegn089520 分钟前
计算机毕业设计|基于springboot + vue二手家电管理系统(源码+数据库+文档)
vue.js·spring boot·后端·课程设计