Java原子类详解
文章目录
- 一、原子类核心概念与原理
- 1.1 什么是原子操作
- 1.2 CAS(Compare-And-Swap)核心原理
- 1.3 原子类 vs 同步锁
- 二、原子类分类
- 2.1 基本类型原子类
- 2.1.1 AtomicInteger
- 2.1.2 AtomicLong & AtomicBoolean
- 2.2 引用类型原子类
- 2.2.1 AtomicReference
- 2.2.2 解决ABA问题
- 2.2.3 数组类型原子类
- 2.3 字段更新原子类
- AtomicIntegerFieldUpdater
- 2.4 数组类型原子类
- 2.5 高性能累加器(JDK 8+)
- 2.5.1 LongAdder
- 2.5.2 LongAdder vs AtomicLong
- 2.6 LongAccumulator
- 2.6.1 核心设计与特点
- 2.6.2 构造方法
- 2.6.3 常用方法
- 2.6.4 与AtomicLong对比
- 2.6.5 使用示例
- 2.6.6 注意事项
- 2.7 示例
- 三、原子类底层实现剖析
- 3.1 Unsafe类
- 3.2 CAS操作图解
- 四、原子类典型应用场景
- 4.1 高性能计数器
- 4.2 无锁栈实现
- 4.3 状态标志管理
- 五、原子类最佳实践与注意事项
- 5.1 最佳实践
- 5.2 性能优化建议
- 5.3 常见陷阱
- 5.3.1 复合操作问题
- 5.3.2 版本号溢出问题
- 六、原子类在JUC中的应用
- 七、总结
一、原子类核心概念与原理
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->AatomicRef.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]}
}
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 int ) get(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 核心设计与特点
-
分段竞争(Cells 数组)
LongAccumulator
内部维护一个Cell
数组(分段)和一个基础值base
。每个Cell
包含一个long
类型的值,用于存储部分累加结果。- 当线程竞争较小时,操作直接作用于
base
; - 当竞争激烈时,线程会被分散到不同的
Cell
上进行操作,减少 CAS 冲突,提高并发效率。
- 当线程竞争较小时,操作直接作用于
-
自定义累加器函数
与AtomicLong
固定支持自增 / 自减不同,LongAccumulator
允许通过构造函数传入一个LongBinaryOperator
函数(二元操作符),灵活定义累加逻辑(如求和、取最大值、取最小值等)。 -
延迟初始化 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() | 重置所有值为初始值 identity (base 和 Cell 均重置)。 |
long getThenReset() | 先获取当前结果,再重置(原子操作,适合需要周期性获取并重置的场景)。 |
2.6.4 与AtomicLong对比
特性 | AtomicLong | LongAccumulator |
---|---|---|
操作逻辑 | 固定为自增 / 自减等简单操作 | 支持自定义累加函数(灵活) |
并发处理 | 单一变量,高并发下 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. 创建累加器:求和逻辑,初始值为 0LongAccumulator 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 注意事项
- 自定义函数的约束
累加函数需满足结合律和交换律,否则多线程并发累加可能导致结果错误(因不同线程的操作顺序不固定)。 - 内存可见性
get()
方法会强制读取最新的base
和Cell
值(通过volatile
保证),确保结果的内存可见性。 - 性能优势场景
在低并发场景下,LongAccumulator
与AtomicLong
性能差异不大;但在高并发(多线程频繁更新)时,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 最佳实践
- 优先使用JDK8+的累加器:
在超高并发计数场景使用LongAdder
- 避免复杂操作:原子类适合简单操作,复杂逻辑应使用锁
- 结合volatile使用:确保可见性
- 注意ABA问题:使用AtomicStampedReference解决
- 合理选择类型:根据需求选择基本类型或引用类型
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并发工具包中广泛使用原子类:
- ConcurrentHashMap:使用
sun.misc.Unsafe
和原子操作实现并发控制 - AQS(AbstractQueuedSynchronizer):同步框架核心,使用原子状态
- 线程池状态管理:ThreadPoolExecutor使用原子整数管理状态
- ForkJoinPool:使用LongAdder进行任务计数
七、总结
Java的java.util.concurrent.atomic
包提供了一系列高效的原子操作类:
- 基于CAS实现,提供无锁线程安全操作
- 分为基本类型、引用类型、数组类型和字段更新器
- JDK8+新增高性能累加器(LongAdder等)
- 解决ABA问题需使用版本号机制
- 适用于计数器、状态标志、简单共享变量等场景
- 在低竞争环境下性能优于锁,但在高竞争下需谨慎使用
资料获取:
- 通过网盘分享的文件:
- 链接: https://pan.baidu.com/s/1OjM6cirQNH_dE7ykKEXpVA 提取码: 2gda