介绍java中atomic及相关类
文章目录
- 一、Atomic 类的核心原理
- 二、常见 Atomic 类及用法
- 1. 基本类型原子类
- (1)`AtomicInteger`(原子更新 int)
- (2)`AtomicLong`(原子更新 long)
- (3)`AtomicBoolean`(原子更新 boolean)
- 2. 引用类型原子类
- (1)`AtomicReference<V>`(原子更新对象引用)
- (2)`AtomicStampedReference<V>`(解决 ABA 问题)
- (3)`AtomicMarkableReference<V>`(标记是否修改)
- 3. 数组类型原子类
- (1)`AtomicIntegerArray`(原子更新 int 数组)
- (2)`AtomicLongArray` 和 `AtomicReferenceArray`
- 4. 字段更新器(FieldUpdaters)
- (1)`AtomicIntegerFieldUpdater`(更新对象的 int 字段)
- 三、Atomic 类的适用场景
- 四、局限性
- 总结
Java 中的
java.util.concurrent.atomic
包提供了一组原子操作类,用于实现多线程环境下的无锁线程安全编程。这些类基于硬件提供的CAS(Compare-And-Swap,比较并交换) 指令实现,避免了传统锁机制的性能开销,适用于简单值的并发更新场景(如计数器、标志位等)。
一、Atomic 类的核心原理
Atomic 类的线程安全依赖 CAS 操作,其基本逻辑如下:
- 读取当前内存中的值(
V
)。- 计算目标值(
N
,基于当前值的更新结果)。- 用 CAS 指令比较内存中的值是否仍为
V
:
- 若是,将内存值更新为
N
,操作成功。- 若否(被其他线程修改),不做操作,重试或放弃(通常会循环重试)。
CAS 是硬件级别的原子操作,无需加锁即可保证操作的原子性,因此性能优于
synchronized
或ReentrantLock
。
二、常见 Atomic 类及用法
atomic
包提供了针对不同数据类型的原子类,可分为以下几类:
1. 基本类型原子类
用于对
int
、long
等基本类型进行原子更新。
(1)AtomicInteger
(原子更新 int)
核心方法:
getAndIncrement()
:自增 1,返回更新前的值(类似i++
)。incrementAndGet()
:自增 1,返回更新后的值(类似++i
)。getAndAdd(int delta)
:增加指定值,返回更新前的值。compareAndSet(int expect, int update)
:若当前值等于expect
,则更新为update
,返回是否成功。
示例:
AtomicInteger count = new AtomicInteger(0);// 自增(线程安全)
count.incrementAndGet(); // 结果为 1// 条件更新
boolean success = count.compareAndSet(1, 100); // 成功,返回 true,值变为 100
(2)AtomicLong
(原子更新 long)
用法与
AtomicInteger
类似,用于long
类型的原子操作。在 32 位 JVM 上,long
的非原子操作可能存在拆分风险,AtomicLong
可保证其原子性。
(3)AtomicBoolean
(原子更新 boolean)
用于原子更新布尔值,常用
compareAndSet
实现线程安全的开关控制:
AtomicBoolean flag = new AtomicBoolean(false);// 若当前为 false,则更新为 true
if (flag.compareAndSet(false, true)) {System.out.println("执行初始化操作");
}
2. 引用类型原子类
用于原子更新对象引用,支持更复杂的并发场景。
(1)AtomicReference<V>
(原子更新对象引用)
可对任意对象引用进行原子操作,例如原子更新用户对象:
class User {String name;public User(String name) { this.name = name; }
}AtomicReference<User> userRef = new AtomicReference<>(new User("Alice"));// 原子更新用户(若当前是 Alice,则改为 Bob)
User oldUser = new User("Alice");
User newUser = new User("Bob");
boolean swapped = userRef.compareAndSet(oldUser, newUser); // 成功,返回 true
(2)AtomicStampedReference<V>
(解决 ABA 问题)
AtomicReference
存在 ABA 问题(值从 A 变为 B 再变回 A,CAS 会误认为未修改)。AtomicStampedReference
通过版本号(stamp)解决此问题,每次更新时版本号递增:
// 初始化:值为 "A",版本号为 0
AtomicStampedReference<String> ref = new AtomicStampedReference<>("A", 0);int[] stampHolder = new int[1];
String current = ref.get(stampHolder); // current = "A", stampHolder[0] = 0// 只有当值为 "A" 且版本号为 0 时,才更新为 "B",版本号+1
boolean success = ref.compareAndSet("A", "B", 0, 1); // 成功
(3)AtomicMarkableReference<V>
(标记是否修改)
与
AtomicStampedReference
类似,但用布尔值(mark)标记是否被修改,而非版本号,适用于只需判断“是否被修改”的场景。
3. 数组类型原子类
用于原子更新数组中的元素。
(1)AtomicIntegerArray
(原子更新 int 数组)
int[] arr = {1, 2, 3};
AtomicIntegerArray atomicArr = new AtomicIntegerArray(arr);// 原子更新索引 1 的元素(加 5)
atomicArr.addAndGet(1, 5); // 数组变为 [1, 7, 3]// 条件更新索引 0 的元素
atomicArr.compareAndSet(0, 1, 10); // 索引 0 的值从 1 变为 10
(2)AtomicLongArray
和 AtomicReferenceArray
分别用于原子更新
long
数组和对象引用数组,用法与AtomicIntegerArray
类似。
4. 字段更新器(FieldUpdaters)
用于原子更新对象的非静态字段(需字段可见性为
volatile
),无需修改类定义即可实现原子操作。
(1)AtomicIntegerFieldUpdater
(更新对象的 int 字段)
示例:
class Student {volatile int score; // 必须是 volatile 修饰的非静态字段public Student(int score) { this.score = score; }
}// 创建更新器(指定类和字段名)
AtomicIntegerFieldUpdater<Student> updater = AtomicIntegerFieldUpdater.newUpdater(Student.class, "score");Student student = new Student(80);
// 原子更新 score 字段(加 10)
updater.addAndGet(student, 10); // score 变为 90
类似的还有
AtomicLongFieldUpdater
和AtomicReferenceFieldUpdater
,用于更新long
字段和对象字段。
三、Atomic 类的适用场景
- 计数器:如接口调用次数、并发任务数统计(
AtomicInteger
、AtomicLong
)。- 标志位:如线程安全的开关控制(
AtomicBoolean
)。- 无锁数据结构:如实现非阻塞队列、栈等(基于
AtomicReference
)。- 乐观锁场景:通过版本号控制(
AtomicStampedReference
)实现并发更新。
四、局限性
- 仅支持简单操作:适合单一变量的原子更新,复杂逻辑(如多变量联动)仍需锁机制。
- 可能导致自旋消耗:CAS 失败时会循环重试,高并发下可能浪费 CPU 资源。
- 无法解决所有并发问题:如需要互斥的场景(如“检查-修改-操作”三步原子性),可能仍需锁。
总结
atomic
包提供的原子类通过 CAS 操作实现了高效的线程安全,适合简单值的并发更新场景,性能优于传统锁机制。但需注意其局限性:仅适用于单一变量操作,复杂场景仍需结合锁或其他并发工具。实际开发中,应根据业务复杂度选择合适的并发方案。