Java原子类详解

一、原子类核心概念与原理

1.1 什么是原子操作

原子操作是指不可中断的一个或一系列操作,这些操作要么全部执行成功,要么全部不执行。在并发编程中,原子操作保证了多线程环境下对共享变量的操作不会出现数据不一致的问题。

1.2 CAS(Compare-And-Swap)核心原理

原子类的底层实现依赖于CAS操作,这是一种无锁(lock-free)算法:

在这里插入图片描述

CAS操作包含三个操作数:

  • 内存位置(V)
  • 预期原值(A)
  • 新值(B)

当且仅当V的值等于A时,CAS才会将V的值设为B,否则不执行任何操作。整个操作是原子性的。

1.3 原子类 vs 同步锁

特性原子类同步锁(synchronized)
实现机制CAS无锁算法阻塞式锁
性能高(非阻塞)低(上下文切换开销)
适用场景简单原子操作复杂临界区
死锁风险
内存开销较高
线程阻塞不会阻塞可能阻塞

二、原子类分类

2.1 基本类型原子类

AtomicInteger原子操作int类型变量get():获取当前值 set(int newValue):设置新值 getAndIncrement():先获取当前值,再自增 incrementAndGet():先自增,再获取新值 getAndSet(int newValue):先获取当前值,再设置新值 compareAndSet(int expect, int update):如果当前值等于expect,则更新为update,返回是否成功
AtomicLong原子操作long类型变量AtomicInteger类似,方法名相同(如getAndIncrement()incrementAndGet()等),仅操作类型为long
AtomicBoolean原子操作boolean类型变量get():获取当前值 set(boolean newValue):设置新值 getAndSet(boolean newValue):先获取当前值,再设置新值 compareAndSet(boolean expect, boolean update):如果当前值等于expect,则更新为update,返回是否成功

2.1.1 AtomicInteger

package cn.tcmeta.atomic;

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicIntegerDemo {
    private static final AtomicInteger counter = new AtomicInteger(0);
    
    public static void main(String[] args) throws InterruptedException {
        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                // 原子自增
                counter.incrementAndGet();
            }
        };
        
        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);
        
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        
        System.out.println("Final count: " + counter.get()); // 2000
    }
}

在这里插入图片描述

核心方法

  • get():获取当前值
  • set(int newValue):设置新值
  • getAndIncrement():先获取当前值再自增(i++)
  • incrementAndGet():先自增再获取值(++i)
  • compareAndSet(int expect, int update):CAS操作
  • updateAndGet(IntUnaryOperator updateFunction):JDK8+函数式更新

2.1.2 AtomicLong & AtomicBoolean

用法类似AtomicInteger,用于长整型和布尔类型。

2.2 引用类型原子类

类名特点常用操作方法
AtomicReference<V>原子操作对象引用(泛型)get():获取当前引用 set(V newValue):设置新引用 getAndSet(V newValue):先获取当前引用,再设置新引用 compareAndSet(V expect, V update):如果当前引用等于expect,则更新为update
AtomicStampedReference<V>解决 ABA 问题的引用原子类(带版本号)getStamp():获取当前版本号 getReference():获取当前引用 compareAndSet(V expect, V update, int expectedStamp, int newStamp):如果引用和版本号都匹配,则更新引用和版本号 get():返回包含当前引用和版本号的数组
AtomicMarkableReference<V>带标记的引用原子类(标记为boolean,简化版版本控制)isMarked():获取当前标记 getReference():获取当前引用 compareAndSet(V expect, V update, boolean expectedMark, boolean newMark):如果引用和标记都匹配,则更新引用和标记

2.2.1 AtomicReference

package cn.tcmeta.atomic;

import java.util.concurrent.atomic.AtomicReference;

class User {
    String name;
    int age;
    
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

public class AtomicReferenceDemo {
    public static void main(String[] args) {
        User user1 = new User("Alice", 25);
        User user2 = new User("Bob", 30);
        
        AtomicReference<User> atomicUser = new AtomicReference<>(user1);
        
        // CAS更新引用
        boolean success = atomicUser.compareAndSet(user1, user2);
        System.out.println("Update success: " + success); // true
        
        // 获取当前值
        User current = atomicUser.get();
        System.out.println("Current user: " + current.name); // Bob
    }
}

2.2.2 解决ABA问题

ABA问题:值从A变成B又变回A,CAS会认为没有变化

解决方案:

import java.util.concurrent.atomic.AtomicStampedReference;

public class ABASolution {
    public static void main(String[] args) {
        String initialRef = "A";
        int initialStamp = 0;
        
        AtomicStampedReference<String> atomicRef = 
            new AtomicStampedReference<>(initialRef, initialStamp);
        
        // 线程1尝试修改
        new Thread(() -> {
            int[] stampHolder = new int[1];
            String currentRef = atomicRef.get(stampHolder);
            int currentStamp = stampHolder[0];
            
            // 模拟ABA:A->B->A
            atomicRef.compareAndSet("A", "B", currentStamp, currentStamp + 1);
            atomicRef.compareAndSet("B", "A", currentStamp + 1, currentStamp + 2);
        }).start();
        
        // 线程2尝试修改
        new Thread(() -> {
            try {
                Thread.sleep(500); // 等待ABA发生
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            int[] stampHolder = new int[1];
            String currentRef = atomicRef.get(stampHolder);
            int currentStamp = stampHolder[0];
            
            // 使用版本号检测ABA问题
            boolean success = atomicRef.compareAndSet(
                "A", "C", 
                initialStamp, // 预期版本号 
                currentStamp + 1
            );
            
            System.out.println("Update success: " + success); // false
        }).start();
    }
}

2.2.3 数组类型原子类

类名特点常用操作方法
AtomicIntegerArray原子操作int[]数组get(int index):获取指定索引的元素 set(int index, int newValue):设置指定索引的元素 getAndIncrement(int index):指定索引元素先获取再自增 incrementAndGet(int index):指定索引元素先自增再获取 compareAndSet(int index, int expect, int update):如果指定索引元素等于expect,则更新为update
AtomicLongArray原子操作long[]数组AtomicIntegerArray类似,操作类型为long数组
AtomicReferenceArray原子操作对象数组(Object[]方法与上述数组原子类类似,但操作的是对象引用,如get(int index)set(int index, E newValue)compareAndSet(int index, E expect, E update)
import java.util.concurrent.atomic.AtomicIntegerArray;

public class AtomicArrayDemo {
    public static void main(String[] args) {
        int[] values = {1, 2, 3};
        AtomicIntegerArray array = new AtomicIntegerArray(values);
        
        // 原子更新数组元素
        array.compareAndSet(1, 2, 20);
        
        System.out.println(array); // [1, 20, 3]
    }
}
  1. AtomicIntegerArray
import java.util.concurrent.atomic.AtomicIntegerArray;

public class AtomicArrayDemo {
    public static void main(String[] args) {
        int[] values = {1, 2, 3};
        AtomicIntegerArray array = new AtomicIntegerArray(values);
        
        // 原子更新数组元素
        array.compareAndSet(1, 2, 20);
        
        System.out.println(array); // [1, 20, 3]
    }
}

2.3 字段更新原子类

类名特点常用操作方法
AtomicIntegerFieldUpdater<T>原子更新对象的int类型字段newUpdater(Class<T> tClass, String fieldName):创建更新器(字段需为volatile intget(T obj):获取对象字段的值 set(T obj, int newValue):设置对象字段的值 getAndIncrement(T obj):对象字段先获取再自增 compareAndSet(T obj, int expect, int update):原子更新字段
AtomicLongFieldUpdater<T>原子更新对象的long类型字段AtomicIntegerFieldUpdater类似,操作volatile long字段
AtomicReferenceFieldUpdater<T, V>原子更新对象的引用类型字段与上述字段更新器类似,操作volatile V类型字段,方法包括get(T obj)set(T obj, V newValue)compareAndSet(T obj, V expect, V update)
AtomicIntegerFieldUpdater
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

public class FieldUpdaterDemo {
    static class Counter {
        // 必须使用volatile修饰
        volatile int count;
    }
    
    public static void main(String[] args) {
        Counter counter = new Counter();
        
        // 创建字段更新器
        AtomicIntegerFieldUpdater<Counter> updater = 
            AtomicIntegerFieldUpdater.newUpdater(Counter.class, "count");
        
        // 原子更新字段
        updater.incrementAndGet(counter);
        
        System.out.println("Count: " + counter.count); // 1
    }
}

2.4 数组类型原子类

  • AtomicIntegerArray
import java.util.concurrent.atomic.AtomicIntegerArray;

public class AtomicArrayDemo {
    public static void main(String[] args) {
        int[] values = {1, 2, 3};
        AtomicIntegerArray array = new AtomicIntegerArray(values);  
        // 原子更新数组元素
        array.compareAndSet(1, 2, 20);
        System.out.println(array); // [1, 20, 3]
    }
}

2.5 高性能累加器(JDK 8+)

2.5.1 LongAdder

import java.util.concurrent.atomic.LongAdder;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class LongAdderDemo {
    public static void main(String[] args) throws InterruptedException {
        LongAdder adder = new LongAdder();
        ExecutorService executor = Executors.newFixedThreadPool(8);
        
        // 100个任务,每个累加1000次
        for (int i = 0; i < 100; i++) {
            executor.submit(() -> {
                for (int j = 0; j < 1000; j++) {
                    adder.increment();
                }
            });
        }
        
        executor.shutdown();
        while (!executor.isTerminated()) {
            Thread.sleep(10);
        }
        
        System.out.println("Final sum: " + adder.sum()); // 100000
    }
}

2.5.2 LongAdder vs AtomicLong

在这里插入图片描述

2.6 LongAccumulator

LongAccumulator 是 Java 8 引入的 java.util.concurrent.atomic 包中的一个高级原子类,专为高效的数值累加(或自定义组合特定操作)设计,尤其适用于多线程并发场景。它扩展了 Striped64 抽象类,通过内部分段(Cells)机制分散竞争,比传统的 AtomicLong 在高并发下具有更高的吞吐量。

2.6.1 核心设计与特点

  1. 分段竞争(Cells 数组)
    LongAccumulator 内部维护一个 Cell 数组(分段)和一个基础值 base。每个 Cell 包含一个 long 类型的值,用于存储部分累加结果。

    • 当线程竞争较小时,操作直接作用于 base
    • 当竞争激烈时,线程会被分散到不同的 Cell 上进行操作,减少 CAS 冲突,提高并发效率。
  2. 自定义累加器函数
    AtomicLong 固定支持自增 / 自减不同,LongAccumulator 允许通过构造函数传入一个 LongBinaryOperator 函数(二元操作符),灵活定义累加逻辑(如求和、取最大值、取最小值等)。

  3. 延迟初始化 Cells
    Cells 数组在首次出现线程竞争时才会初始化,避免了无竞争场景下的资源浪费。

2.6.2 构造方法

// 构造函数:传入累加器函数和初始值
public LongAccumulator(LongBinaryOperator accumulatorFunction, long identity)
  • accumulatorFunction:定义累加规则的函数,接收两个参数(当前值和新值),返回计算结果。
    例如:(x, y) -> x + y 表示求和;(x, y) -> Math.max(x, y) 表示取最大值。
  • identity:初始值(类似累加的 “起点”),需满足 accumulatorFunction(identity, x) = x(如求和时初始值为 0)。

2.6.3 常用方法

方法名功能描述
void accumulate(long x)核心方法:根据自定义函数,将 x 累加到当前结果中(线程安全)。
long get()获取当前累加的最终结果(汇总 base 和所有 Cell 的值)。
void reset()重置所有值为初始值 identitybaseCell 均重置)。
long getThenReset()先获取当前结果,再重置(原子操作,适合需要周期性获取并重置的场景)。

2.6.4 与AtomicLong对比

特性AtomicLongLongAccumulator
操作逻辑固定为自增 / 自减等简单操作支持自定义累加函数(灵活)
并发处理单一变量,高并发下 CAS 冲突频繁分段存储(Cells),分散竞争,高并发性能更优
适用场景简单计数器(如统计请求数)复杂累加逻辑(如求和、求最值、自定义聚合)

2.6.5 使用示例

计算总和.

package cn.tcmeta.atomic;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.stream.IntStream;

public class LongAccumulatorDemo {
    public static void main(String[] args) {
        // 1. 创建累加器:求和逻辑,初始值为 0
        LongAccumulator accumulator = new LongAccumulator(Long::sum, 0);
        
        // 2. 多线程累加 1~1000 的数字
        ExecutorService executor = Executors.newFixedThreadPool(10);
        IntStream.rangeClosed(1, 1000)
                 .forEach(num -> executor.submit(() -> accumulator.accumulate(num)));
        
        // 3. 关闭线程池并等待完成
        executor.shutdown();
        while (!executor.isTerminated()) {}
        
        // 4. 输出结果(预期为 500500)
        System.out.println("总和:" + accumulator.get()); // 输出:总和:500500
    }
}

在这里插入图片描述

2.6.6 注意事项

  1. 自定义函数的约束
    累加函数需满足结合律和交换律,否则多线程并发累加可能导致结果错误(因不同线程的操作顺序不固定)。
  2. 内存可见性
    get() 方法会强制读取最新的 baseCell 值(通过 volatile 保证),确保结果的内存可见性。
  3. 性能优势场景
    在低并发场景下,LongAccumulatorAtomicLong 性能差异不大;但在高并发(多线程频繁更新)时,LongAccumulator 的分段机制能显著减少竞争,提升性能。

2.7 示例

需求: 实现高并发下点赞设计

  • 方案一: 添加同步方法或者同步代码块

  • 方案二: 原子类

  • 方案三: 使用LongAddr

  • 方案四: 使用LongAccumulator

package cn.tcmeta.atomic;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;

/**
 * 高并下的点赞方案对比
 *      1. 同步方法/同步代码块
 *      2. 原子类
 *      3. LongAddr
 *      4. LongAccumulator
 */
public class T1 {
    public static void main(String[] args) throws InterruptedException {
        long start = 0L;
        long end = 0L;
        ClickLikes clickLikes = new ClickLikes();
        // 50个线程
        final int THREAD_COUNT = 50;
        // 每个线程的执行次数
        final int RUN_TIMES = 10000;

        CountDownLatch c1 = new CountDownLatch(THREAD_COUNT);
        CountDownLatch c2 = new CountDownLatch(THREAD_COUNT);
        CountDownLatch c3 = new CountDownLatch(THREAD_COUNT);
        CountDownLatch c4 = new CountDownLatch(THREAD_COUNT);

        start = System.currentTimeMillis();
        for (int i = 0; i < THREAD_COUNT; i++) {
            new Thread(() ->{
               try {
                   for (int i1 = 0; i1 < 100 * RUN_TIMES; i1++) {
                        clickLikes.addLikesBySynchronized();
                   }
               }finally {
                   c1.countDown();
               }
            }, "线程:" + i).start();
        }
        c1.await();
        end = System.currentTimeMillis();
        System.out.println("方案一: synchronized 共花费: " + (end - start) + " -- 结果是: " + clickLikes.count);

        start = System.currentTimeMillis();
        for (int i = 0; i < THREAD_COUNT; i++) {
            new Thread(() ->{
                try {
                    for (int i1 = 0; i1 < 100 * RUN_TIMES; i1++) {
                        clickLikes.addLikesByAtomicInteger();
                    }
                }finally {
                    c2.countDown();
                }
            }, "线程:" + i).start();
        }
        c2.await();
        end = System.currentTimeMillis();
        System.out.println("方案二: atomicInteger 共花费: " + (end - start) + " -- 结果是: " + clickLikes.count);


        start = System.currentTimeMillis();
        for (int i = 0; i < THREAD_COUNT; i++) {
            new Thread(() ->{
                try {
                    for (int i1 = 0; i1 < 100 * RUN_TIMES; i1++) {
                        clickLikes.addLikesByLongAddr();
                    }
                }finally {
                    c3.countDown();
                }
            }, "线程:" + i).start();
        }
        c3.await();
        end = System.currentTimeMillis();
        System.out.println("方案三: LongAddr 共花费: " + (end - start) + " -- 结果是: " + clickLikes.count);


        start = System.currentTimeMillis();
        for (int i = 0; i < THREAD_COUNT; i++) {
            new Thread(() ->{
                try {
                    for (int i1 = 0; i1 < 100 * RUN_TIMES; i1++) {
                        clickLikes.addLikesByLongAccumulator();
                    }
                }finally {
                    c4.countDown();
                }
            }, "线程:" + i).start();
        }
        c4.await();
        end = System.currentTimeMillis();
        System.out.println("方案四: LongAccumulator 共花费: " + (end - start) + " -- 结果是: " + clickLikes.count);
    }
}

class ClickLikes {
    int count = 0;
    private final AtomicInteger atomicInteger = new AtomicInteger(0);
    private final LongAdder longAdder = new LongAdder();
    private final LongAccumulator longAccumulator = new LongAccumulator(Long::sum, 0);
    /**
     * 方案一,同步方法.
     */
    public synchronized void addLikesBySynchronized(){
        count ++;
    }

    /**
     * 方案二: 使用原子类
     */
    public void addLikesByAtomicInteger(){
        atomicInteger.getAndIncrement();
    }

    /**
     * 方案三: LongAddr, 高性能
     */
    public void addLikesByLongAddr(){
        longAdder.increment();
    }

    /**
     * 方案四: LongAccumulator,高性能.
     */
    public void addLikesByLongAccumulator(){
        longAccumulator.accumulate(1);
    }

}

在这里插入图片描述

三、原子类底层实现剖析

3.1 Unsafe类

原子类的核心实现依赖于sun.misc.Unsafe类,它提供了硬件级别的原子操作:

Unsafe unsafe = Unsafe.getUnsafe();
public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;
    private static final Unsafe U = Unsafe.getUnsafe();
    
    private static final long VALUE
        = U.objectFieldOffset(AtomicInteger.class, "value");

    private volatile int value;
    
    public final boolean compareAndSet(int expectedValue, int newValue) {
        return U.compareAndSetInt(this, VALUE, expectedValue, newValue);
    }
    
}

3.2 CAS操作图解

在这里插入图片描述

四、原子类典型应用场景

4.1 高性能计数器

class RequestCounter {
    private final LongAdder totalRequests = new LongAdder();
    private final LongAdder failedRequests = new LongAdder();
    
    public void incrementTotal() {
        totalRequests.increment();
    }
    
    public void incrementFailed() {
        failedRequests.increment();
    }
    
    public double getSuccessRate() {
        long total = totalRequests.sum();
        long failed = failedRequests.sum();
        return (total - failed) * 100.0 / total;
    }
}

4.2 无锁栈实现

import java.util.concurrent.atomic.AtomicReference;

public class LockFreeStack<T> {
    private static class Node<T> {
        final T value;
        Node<T> next;
        
        public Node(T value) {
            this.value = value;
        }
    }
    
    private final AtomicReference<Node<T>> top = new AtomicReference<>();
    
    public void push(T value) {
        Node<T> newNode = new Node<>(value);
        Node<T> oldTop;
        do {
            oldTop = top.get();
            newNode.next = oldTop;
        } while (!top.compareAndSet(oldTop, newNode));
    }
    
    public T pop() {
        Node<T> oldTop;
        Node<T> newTop;
        do {
            oldTop = top.get();
            if (oldTop == null) {
                return null;
            }
            newTop = oldTop.next;
        } while (!top.compareAndSet(oldTop, newTop));
        
        return oldTop.value;
    }
}

4.3 状态标志管理

public class ConnectionManager {
    private final AtomicBoolean connected = new AtomicBoolean(false);
    
    public void connect() {
        if (connected.compareAndSet(false, true)) {
            // 执行连接逻辑
            System.out.println("Connected successfully");
        }
    }
    
    public void disconnect() {
        if (connected.compareAndSet(true, false)) {
            // 执行断开逻辑
            System.out.println("Disconnected successfully");
        }
    }
}

五、原子类最佳实践与注意事项

5.1 最佳实践

  1. 优先使用JDK8+的累加器在超高并发计数场景使用LongAdder
  2. 避免复杂操作:原子类适合简单操作,复杂逻辑应使用锁
  3. 结合volatile使用:确保可见性
  4. 注意ABA问题:使用AtomicStampedReference解决
  5. 合理选择类型:根据需求选择基本类型或引用类型

5.2 性能优化建议

// 不推荐:多次调用get()增加开销
if (atomicInt.get() > 0) {
    atomicInt.decrementAndGet();
}

// 推荐:使用单一原子操作
atomicInt.updateAndGet(x -> x > 0 ? x - 1 : x);

5.3 常见陷阱

5.3.1 复合操作问题

// 错误:检查后更新不是原子的
if (atomicInt.get() < MAX) {
    atomicInt.incrementAndGet();
}

// 正确:使用原子方法
atomicInt.updateAndGet(x -> x < MAX ? x + 1 : x);

5.3.2 版本号溢出问题

// 使用AtomicStampedReference时注意版本号范围
private static final int MAX_STAMP = Integer.MAX_VALUE;

public void updateRef(Object newRef, AtomicStampedReference<Object> ref) {
    int[] stampHolder = new int[1];
    Object currentRef;
    int currentStamp;
    
    do {
        currentRef = ref.get(stampHolder);
        currentStamp = stampHolder[0];
        // 处理版本号溢出
        int newStamp = (currentStamp == MAX_STAMP) ? 0 : currentStamp + 1;
    } while (!ref.compareAndSet(currentRef, newRef, currentStamp, newStamp));
}

六、原子类在JUC中的应用

Java并发工具包中广泛使用原子类:

  1. ConcurrentHashMap:使用sun.misc.Unsafe和原子操作实现并发控制
  2. AQS(AbstractQueuedSynchronizer):同步框架核心,使用原子状态
  3. 线程池状态管理:ThreadPoolExecutor使用原子整数管理状态
  4. ForkJoinPool:使用LongAdder进行任务计数

七、总结

Java的java.util.concurrent.atomic包提供了一系列高效的原子操作类:

  • 基于CAS实现,提供无锁线程安全操作
  • 分为基本类型、引用类型、数组类型和字段更新器
  • JDK8+新增高性能累加器(LongAdder等)
  • 解决ABA问题需使用版本号机制
  • 适用于计数器、状态标志、简单共享变量等场景
  • 在低竞争环境下性能优于锁,但在高竞争下需谨慎使用

资料获取:

  • 通过网盘分享的文件:
  • 链接: https://pan.baidu.com/s/1OjM6cirQNH_dE7ykKEXpVA 提取码: 2gda
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值