Java并发编程利器:深入解析13个原子操作类
在多线程并发环境下,保证数据操作的原子性是个常见且关键的挑战。Java从JDK 1.5开始提供了
java.util.concurrent.atomic包,其中包含13个强大的原子操作类,让我们能够以无锁的方式实现线程安全。本文将带你深入理解这些原子类的原理、API和使用场景。
一、为什么需要原子操作类?
1.1 问题的由来
想象一下这样的场景:多个线程同时操作同一个银行账户进行取款,如果不加控制,可能会出现什么情况?
// 不安全的计数器示例
class UnsafeCounter {
private int count = 0;
public void increment() {
count++; // 这不是原子操作!
}
}
count++看似简单,实际上包含三个步骤:
- 读取count的当前值
- 将值加1
- 将新值写回count
在多线程环境下,这两个步骤可能被其他线程打断,导致数据不一致。
1.2 传统的解决方案及其缺点
传统做法是使用synchronized关键字:
class SynchronizedCounter {
private int count = 0;
public synchronized void increment() {
count++;
}
}
synchronized确实能保证线程安全,但存在以下问题:
- 性能开销:锁的获取和释放需要代价
- 可能死锁:不正确的锁顺序可能导致死锁
- 降低并发性:同一时刻只有一个线程能访问
1.3 原子操作类的优势
原子操作类基于CAS(Compare-And-Swap) 机制,提供了:
- 无锁编程:避免传统锁的开销
- 高性能:在低竞争环境下性能优异
- 无死锁风险:基于硬件指令,不会产生死锁
- 高并发:支持多个线程同时操作
二、原子更新基本类型类
2.1 AtomicBoolean - 原子更新布尔类型
使用场景:状态标志位、开关控制、条件判断
核心API详解
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
get() |
- | boolean |
获取当前值 |
set(boolean newValue) |
newValue: 新值 |
void |
设置新值 |
getAndSet(boolean newValue) |
newValue: 新值 |
boolean |
原子性地设置为新值并返回旧值 |
compareAndSet(boolean expect, boolean update) |
expect: 期望值update: 更新值 |
boolean |
如果当前值等于期望值,则原子性地更新 |
lazySet(boolean newValue) |
newValue: 新值 |
void |
最终设置为新值,但不保证立即可见性 |
weakCompareAndSet(boolean expect, boolean update) |
expect: 期望值update: 更新值 |
boolean |
可能更弱的CAS操作,在某些平台上性能更好 |
import java.util.concurrent.atomic.AtomicBoolean;
/**
* AtomicBoolean示例:用于原子性地更新布尔值
* 典型场景:系统开关、状态标志等
*/
public class AtomicBooleanDemo {
public static void main(String[] args) {
// 创建AtomicBoolean,初始值为false
AtomicBoolean atomicBoolean = new AtomicBoolean(false);
// get(): 获取当前值
System.out.println("初始值: " + atomicBoolean.get());
// getAndSet(): 原子性地设置为true,返回旧值
boolean oldValue = atomicBoolean.getAndSet(true);
System.out.println("getAndSet旧值: " + oldValue + ", 新值: " + atomicBoolean.get());
// compareAndSet(): 比较并设置
boolean success = atomicBoolean.compareAndSet(true, false);
System.out.println("CAS操作结果: " + success + ", 当前值: " + atomicBoolean.get());
// lazySet(): 最终会设置,但不保证立即可见性
atomicBoolean.lazySet(true);
System.out.println("lazySet后的值: " + atomicBoolean.get());
// weakCompareAndSet(): 弱版本CAS
boolean weakSuccess = atomicBoolean.weakCompareAndSet(true, false);
System.out.println("弱CAS操作结果: " + weakSuccess + ", 当前值: " + atomicBoolean.get());
}
}
原理分析:
AtomicBoolean内部实际上使用int类型来存储,0表示false,1表示true。通过compareAndSwapInt来实现原子操作。
2.2 AtomicInteger - 原子更新整型
使用场景:计数器、序列号生成、资源数量控制
核心API详解
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
get() |
- | int |
获取当前值 |
set(int newValue) |
newValue: 新值 |
void |
设置新值 |
getAndSet(int newValue) |
newValue: 新值 |
int |
原子性地设置为新值并返回旧值 |
compareAndSet(int expect, int update) |
expect: 期望值update: 更新值 |
boolean |
CAS操作 |
getAndIncrement() |
- | int |
原子递增,返回旧值 |
getAndDecrement() |
- | int |
原子递减,返回旧值 |
getAndAdd(int delta) |
delta: 增量 |
int |
原子加法,返回旧值 |
incrementAndGet() |
- | int |
原子递增,返回新值 |
decrementAndGet() |
- | int |
原子递减,返回新值 |
addAndGet(int delta) |
delta: 增量 |
int |
原子加法,返回新值 |
updateAndGet(IntUnaryOperator) |
operator: 更新函数 |
int |
函数式更新 |
accumulateAndGet(int x, IntBinaryOperator) |
x: 参数operator: 操作函数 |
int |
累积计算 |
import java.util.concurrent.atomic.AtomicInteger;
/**
* AtomicInteger是最常用的原子类之一
* 适用于计数器、ID生成器等需要原子递增的场景
*/
public class AtomicIntegerDemo {
public static void main(String[] args) {
AtomicInteger atomicInt = new AtomicInteger(0);
// 基础操作
System.out.println("初始值: " + atomicInt.get());
atomicInt.set(5);
System.out.println("set(5)后: " + atomicInt.get());
// 原子递增并返回旧值 - 常用于计数
System.out.println("getAndIncrement: " + atomicInt.getAndIncrement()); // 返回5
System.out.println("当前值: " + atomicInt.get()); // 6
// 原子递减并返回旧值
System.out.println("getAndDecrement: " + atomicInt.getAndDecrement()); // 返回6
System.out.println("当前值: " + atomicInt.get()); // 5
// 原子加法并返回旧值
System.out.println("getAndAdd(10): " + atomicInt.getAndAdd(10)); // 返回5
System.out.println("当前值: " + atomicInt.get()); // 15
// 原子递增并返回新值
System.out.println("incrementAndGet: " + atomicInt.incrementAndGet()); // 16
// 原子加法并返回结果 - 适合批量增加
int result = atomicInt.addAndGet(10);
System.out.println("addAndGet(10)结果: " + result); // 26
// 比较并设置 - 核心CAS操作
boolean updated = atomicInt.compareAndSet(26, 30);
System.out.println("CAS操作结果: " + updated + ", 当前值: " + atomicInt.get());
// 获取并设置新值 - 适合重置操作
int previous = atomicInt.getAndSet(40);
System.out.println("getAndSet旧值: " + previous + ", 新值: " + atomicInt.get());
// JDK8新增:函数式更新 - 更灵活的更新方式
atomicInt.updateAndGet(x -> x * 2);
System.out.println("updateAndGet(*2)后的值: " + atomicInt.get()); // 80
// 累积计算
atomicInt.accumulateAndGet(10, (x, y) -> x + y * 2);
System.out.println("accumulateAndGet后的值: " + atomicInt.get()); // 100
}
}
源码分析:
public final int getAndIncrement() {
// 自旋CAS:循环直到成功
for (;;) {
int current = get(); // 步骤1:获取当前值
int next = current + 1; // 步骤2:计算新值
if (compareAndSet(current, next)) // 步骤3:CAS更新
return current; // 成功则返回旧值
}
// 如果CAS失败,说明有其他线程修改了值,循环重试
}
2.3 AtomicLong - 原子更新长整型
使用场景:大数值计数器、统计信息、唯一ID生成
核心API详解
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
get() |
- | long |
获取当前值 |
set(long newValue) |
newValue: 新值 |
void |
设置新值 |
getAndSet(long newValue) |
newValue: 新值 |
long |
原子性地设置为新值并返回旧值 |
compareAndSet(long expect, long update) |
expect: 期望值update: 更新值 |
boolean |
CAS操作 |
getAndIncrement() |
- | long |
原子递增,返回旧值 |
getAndDecrement() |
- | long |
原子递减,返回旧值 |
getAndAdd(long delta) |
delta: 增量 |
long |
原子加法,返回旧值 |
incrementAndGet() |
- | long |
原子递增,返回新值 |
decrementAndGet() |
- | long |
原子递减,返回新值 |
addAndGet(long delta) |
delta: 增量 |
long |
原子加法,返回新值 |
updateAndGet(LongUnaryOperator) |
operator: 更新函数 |
long |
函数式更新 |
accumulateAndGet(long x, LongBinaryOperator) |
x: 参数operator: 操作函数 |
long |
累积计算 |
import java.util.concurrent.atomic.AtomicLong;
/**
* AtomicLong用于长整型的原子操作
* 在64位系统中性能与AtomicInteger相当
*/
public class AtomicLongDemo {
public static void main(String[] args) {
AtomicLong atomicLong = new AtomicLong(100L);
System.out.println("初始值: " + atomicLong.get());
// 原子递增并返回旧值 - 适合序列号生成
System.out.println("getAndIncrement: " + atomicLong.getAndIncrement());
System.out.println("当前值: " + atomicLong.get());
// 原子递减并返回旧值
System.out.println("getAndDecrement: " + atomicLong.getAndDecrement());
System.out.println("当前值: " + atomicLong.get());
// 原子加法并返回旧值
System.out.println("getAndAdd(50): " + atomicLong.getAndAdd(50L));
System.out.println("当前值: " + atomicLong.get());
// 原子递增并返回新值
System.out.println("incrementAndGet: " + atomicLong.incrementAndGet());
// 原子加法并返回结果 - 适合统计累加
long newValue = atomicLong.addAndGet(50L);
System.out.println("addAndGet(50)结果: " + newValue);
// 比较并设置
boolean success = atomicLong.compareAndSet(250L, 300L);
System.out.println("CAS操作结果: " + success + ", 当前值: " + atomicLong.get());
// JDK8新增:函数式更新
atomicLong.updateAndGet(x -> x / 2);
System.out.println("updateAndGet(/2)后的值: " + atomicLong.get());
// JDK8新增:累积计算 - 适合复杂的原子计算
atomicLong.accumulateAndGet(100L, (x, y) -> x * y);
System.out.println("accumulateAndGet后的值: " + atomicLong.get());
}
}
性能提示:
在32位系统上,AtomicLong的CAS操作可能需要锁住总线,性能相对较差。Java 8提供了LongAdder作为高性能替代方案。
三、原子更新数组类
3.1 AtomicIntegerArray - 原子更新整型数组
使用场景:并发计数器数组、桶统计、并行计算
核心API详解
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
length() |
- | int |
返回数组长度 |
get(int i) |
i: 索引 |
int |
获取指定索引的值 |
set(int i, int newValue) |
i: 索引newValue: 新值 |
void |
设置指定索引的值 |
getAndSet(int i, int newValue) |
i: 索引newValue: 新值 |
int |
原子设置并返回旧值 |
compareAndSet(int i, int expect, int update) |
i: 索引expect: 期望值update: 更新值 |
boolean |
对指定索引进行CAS操作 |
getAndIncrement(int i) |
i: 索引 |
int |
原子递增指定索引,返回旧值 |
getAndDecrement(int i) |
i: 索引 |
int |
原子递减指定索引,返回旧值 |
getAndAdd(int i, int delta) |
i: 索引delta: 增量 |
int |
原子加法,返回旧值 |
incrementAndGet(int i) |
i: 索引 |
int |
原子递增指定索引,返回新值 |
addAndGet(int i, int delta) |
i: 索引delta: 增量 |
int |
原子加法,返回新值 |
import java.util.concurrent.atomic.AtomicIntegerArray;
/**
* AtomicIntegerArray允许原子地更新数组中的单个元素
* 注意:构造函数会复制传入的数组,不影响原数组
*/
public class AtomicIntegerArrayDemo {
public static void main(String[] args) {
int[] initialArray = {1, 2, 3, 4, 5};
// 创建原子整型数组,会复制传入的数组
AtomicIntegerArray atomicArray = new AtomicIntegerArray(initialArray);
System.out.println("数组长度: " + atomicArray.length());
System.out.println("原始数组: " + atomicArray.toString());
// get(): 获取指定索引的值
System.out.println("索引0的值: " + atomicArray.get(0));
// set(): 设置指定索引的值
atomicArray.set(0, 10);
System.out.println("set(0, 10)后的数组: " + atomicArray.toString());
// getAndSet(): 原子更新指定索引的元素并返回旧值
int oldValue = atomicArray.getAndSet(1, 20);
System.out.println("索引1替换前的值: " + oldValue + ", 数组: " + atomicArray.toString());
// getAndIncrement(): 原子递增指定索引的元素 - 适合分桶计数
oldValue = atomicArray.getAndIncrement(2);
System.out.println("索引2递增前值: " + oldValue + ", 数组: " + atomicArray.toString());
// compareAndSet(): 比较并设置特定位置的元素
boolean updated = atomicArray.compareAndSet(3, 4, 40);
System.out.println("索引3 CAS结果: " + updated + ", 数组: " + atomicArray.toString());
// addAndGet(): 原子加法 - 适合累加统计
int newValue = atomicArray.addAndGet(4, 5);
System.out.println("索引4加5后的值: " + newValue + ", 数组: " + atomicArray.toString());
// incrementAndGet(): 原子递增并返回新值
newValue = atomicArray.incrementAndGet(0);
System.out.println("索引0递增后的值: " + newValue);
// 重要:原始数组不会被修改
System.out.println("原始数组值未被修改: " + initialArray[0]); // 仍然是1
}
}
设计思想:
AtomicIntegerArray通过复制数组来避免外部修改,每个数组元素的更新都是独立的原子操作。
3.2 AtomicLongArray - 原子更新长整型数组
使用场景:大数据统计、时间戳数组、大数值桶统计
核心API详解
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
length() |
- | int |
返回数组长度 |
get(int i) |
i: 索引 |
long |
获取指定索引的值 |
set(int i, long newValue) |
i: 索引newValue: 新值 |
void |
设置指定索引的值 |
getAndSet(int i, long newValue) |
i: 索引newValue: 新值 |
long |
原子设置并返回旧值 |
compareAndSet(int i, long expect, long update) |
i: 索引expect: 期望值update: 更新值 |
boolean |
对指定索引进行CAS操作 |
getAndAdd(int i, long delta) |
i: 索引delta: 增量 |
long |
原子加法,返回旧值 |
addAndGet(int i, long delta) |
i: 索引delta: 增量 |
long |
原子加法,返回新值 |
import java.util.concurrent.atomic.AtomicLongArray;
/**
* AtomicLongArray提供长整型数组的原子操作
* 适用于需要大数值范围的并发统计
*/
public class AtomicLongArrayDemo {
public static void main(String[] args) {
long[] initialArray = {100L, 200L, 300L, 400L, 500L};
AtomicLongArray atomicLongArray = new AtomicLongArray(initialArray);
System.out.println("数组长度: " + atomicLongArray.length());
System.out.println("初始数组: " + atomicLongArray.toString());
// 基础操作
System.out.println("索引0的值: " + atomicLongArray.get(0));
atomicLongArray.set(0, 150L);
System.out.println("set(0, 150)后的数组: " + atomicLongArray.toString());
// 原子更新操作
long oldValue = atomicLongArray.getAndSet(1, 250L);
System.out.println("索引1替换前的值: " + oldValue + ", 数组: " + atomicLongArray.toString());
// 原子加法操作
atomicLongArray.getAndAdd(2, 100L);
System.out.println("索引2加100后的数组: " + atomicLongArray.toString());
// 比较并设置
atomicLongArray.compareAndSet(3, 400L, 450L);
System.out.println("索引3 CAS后的数组: " + atomicLongArray.toString());
// 加法并获取新值
long newValue = atomicLongArray.addAndGet(4, 200L);
System.out.println("索引4加200后的值: " + newValue);
}
}
3.3 AtomicReferenceArray - 原子更新引用类型数组
使用场景:对象池、缓存数组、并发数据结构
核心API详解
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
length() |
- | int |
返回数组长度 |
get(int i) |
i: 索引 |
E |
获取指定索引的引用 |
set(int i, E newValue) |
i: 索引newValue: 新引用 |
void |
设置指定索引的引用 |
getAndSet(int i, E newValue) |
i: 索引newValue: 新引用 |
E |
原子设置并返回旧引用 |
compareAndSet(int i, E expect, E update) |
i: 索引expect: 期望引用update: 更新引用 |
boolean |
对指定索引进行CAS操作 |
lazySet(int i, E newValue) |
i: 索引newValue: 新引用 |
void |
延迟设置引用 |
import java.util.concurrent.atomic.AtomicReferenceArray;
/**
* AtomicReferenceArray用于原子更新引用类型数组
* 适用于对象引用需要原子更新的场景
*/
public class AtomicReferenceArrayDemo {
static class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + "(" + age + ")";
}
}
public static void main(String[] args) {
Person[] persons = {
new Person("Alice", 25),
new Person("Bob", 30),
new Person("Charlie", 35),
new Person("David", 40)
};
AtomicReferenceArray<Person> atomicArray = new AtomicReferenceArray<>(persons);
System.out.println("数组长度: " + atomicArray.length());
System.out.println("初始数组: ");
for (int i = 0; i < atomicArray.length(); i++) {
System.out.println("索引 " + i + ": " + atomicArray.get(i));
}
// 原子更新引用 - 适合对象替换
Person newPerson = new Person("Eve", 28);
Person oldPerson = atomicArray.getAndSet(1, newPerson);
System.out.println("索引1替换: " + oldPerson + " -> " + atomicArray.get(1));
// 比较并设置引用
boolean success = atomicArray.compareAndSet(2, persons[2], new Person("Frank", 45));
System.out.println("索引2 CAS结果: " + success + ", 新值: " + atomicArray.get(2));
// 延迟设置
atomicArray.lazySet(3, new Person("Grace", 50));
System.out.println("索引3延迟设置后的值: " + atomicArray.get(3));
// 遍历数组
System.out.println("最终数组状态:");
for (int i = 0; i < atomicArray.length(); i++) {
System.out.println("索引 " + i + ": " + atomicArray.get(i));
}
}
}
四、原子更新引用类型
4.1 AtomicReference - 原子更新引用类型
使用场景:单例模式、缓存更新、状态对象替换
核心API详解
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
get() |
- | V |
获取当前引用 |
set(V newValue) |
newValue: 新引用 |
void |
设置新引用 |
getAndSet(V newValue) |
newValue: 新引用 |
V |
原子设置并返回旧引用 |
compareAndSet(V expect, V update) |
expect: 期望引用update: 更新引用 |
boolean |
CAS操作 |
weakCompareAndSet(V expect, V update) |
expect: 期望引用update: 更新引用 |
boolean |
弱版本CAS |
lazySet(V newValue) |
newValue: 新引用 |
void |
延迟设置引用 |
updateAndGet(UnaryOperator<V>) |
operator: 更新函数 |
V |
函数式更新 |
getAndUpdate(UnaryOperator<V>) |
operator: 更新函数 |
V |
函数式更新并返回旧值 |
accumulateAndGet(V x, BinaryOperator<V>) |
x: 参数operator: 操作函数 |
V |
累积计算 |
import java.util.concurrent.atomic.AtomicReference;
/**
* AtomicReference用于原子更新对象引用
* 解决"先检查后执行"的竞态条件
*/
public class AtomicReferenceDemo {
static class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
@Override
public String toString() {
return "User{name='" + name + "', age=" + age + "}";
}
}
public static void main(String[] args) {
AtomicReference<User> atomicUser = new AtomicReference<>();
User initialUser = new User("张三", 25);
atomicUser.set(initialUser);
System.out.println("初始用户: " + atomicUser.get());
// getAndSet(): 原子更新引用 - 适合缓存更新
User newUser = new User("李四", 30);
User oldUser = atomicUser.getAndSet(newUser);
System.out.println("替换前的用户: " + oldUser);
System.out.println("当前用户: " + atomicUser.get());
// compareAndSet(): 比较并设置 - 核心操作
boolean success = atomicUser.compareAndSet(newUser, new User("王五", 35));
System.out.println("CAS操作结果: " + success + ", 当前用户: " + atomicUser.get());
// weakCompareAndSet(): 弱版本CAS
boolean weakSuccess = atomicUser.weakCompareAndSet(
atomicUser.get(), new User("赵六", 40));
System.out.println("弱CAS操作结果: " + weakSuccess + ", 当前用户: " + atomicUser.get());
// lazySet(): 延迟设置
atomicUser.lazySet(new User("孙七", 45));
System.out.println("延迟设置后的用户: " + atomicUser.get());
// JDK8新增:函数式更新
atomicUser.updateAndGet(user -> new User(user.getName() + "_updated", user.getAge() + 1));
System.out.println("函数式更新后的用户: " + atomicUser.get());
// getAndUpdate(): 函数式更新并返回旧值
User previous = atomicUser.getAndUpdate(user -> new User("周八", 50));
System.out.println("更新前的用户: " + previous + ", 当前用户: " + atomicUser.get());
// accumulateAndGet(): 累积计算
atomicUser.accumulateAndGet(new User("吴九", 55),
(old, param) -> new User(old.getName() + "&" + param.getName(),
old.getAge() + param.getAge()));
System.out.println("累积计算后的用户: " + atomicUser.get());
}
}
典型应用:单例模式的双重检查锁定
class Singleton {
private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<>();
public static Singleton getInstance() {
for (;;) {
Singleton current = INSTANCE.get();
if (current != null) return current;
current = new Singleton();
if (INSTANCE.compareAndSet(null, current)) {
return current;
}
}
}
}
4.2 AtomicMarkableReference - 带标记位的原子引用
使用场景:带状态的缓存、ABA问题简单解决方案
核心API详解
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
getReference() |
- | V |
获取当前引用 |
isMarked() |
- | boolean |
获取当前标记位 |
get(boolean[] markHolder) |
markHolder: 标记位容器 |
V |
获取引用和标记位 |
set(V newReference, boolean newMark) |
newReference: 新引用newMark: 新标记 |
void |
设置引用和标记位 |
compareAndSet(V expectedReference, V newReference, boolean expectedMark, boolean newMark) |
expectedReference: 期望引用newReference: 新引用expectedMark: 期望标记newMark: 新标记 |
boolean |
同时比较引用和标记位 |
attemptMark(V expectedReference, boolean newMark) |
expectedReference: 期望引用newMark: 新标记 |
boolean |
尝试只更新标记位 |
import java.util.concurrent.atomic.AtomicMarkableReference;
/**
* AtomicMarkableReference将引用与一个布尔标记位绑定
* 适用于需要同时更新引用和状态的场景
*/
public class AtomicMarkableReferenceDemo {
public static void main(String[] args) {
String initialRef = "初始数据";
boolean initialMark = false;
// 创建带标记位的原子引用
AtomicMarkableReference<String> atomicMarkableRef =
new AtomicMarkableReference<>(initialRef, initialMark);
System.out.println("初始引用: " + atomicMarkableRef.getReference());
System.out.println("初始标记: " + atomicMarkableRef.isMarked());
// get(boolean[]): 同时获取引用和标记位
boolean[] markHolder = new boolean[1];
String currentRef = atomicMarkableRef.get(markHolder);
System.out.println("当前引用: " + currentRef + ", 当前标记: " + markHolder[0]);
// compareAndSet(): 尝试同时更新引用和标记位
String newRef = "新数据";
boolean newMark = true;
boolean success = atomicMarkableRef.compareAndSet(
initialRef, newRef, initialMark, newMark);
System.out.println("CAS操作结果: " + success);
System.out.println("新引用: " + atomicMarkableRef.getReference());
System.out.println("新标记: " + atomicMarkableRef.isMarked());
// attemptMark(): 只尝试更新标记位
boolean markUpdated = atomicMarkableRef.attemptMark(newRef, false);
System.out.println("标记更新结果: " + markUpdated);
System.out.println("最终标记: " + atomicMarkableRef.isMarked());
// set(): 直接设置引用和标记位
atomicMarkableRef.set("最终数据", true);
System.out.println("直接设置后的引用: " + atomicMarkableRef.getReference());
System.out.println("直接设置后的标记: " + atomicMarkableRef.isMarked());
}
}
4.3 AtomicStampedReference - 带版本号的原子引用
使用场景:解决ABA问题、乐观锁实现
核心API详解
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
getReference() |
- | V |
获取当前引用 |
getStamp() |
- | int |
获取当前版本号 |
get(int[] stampHolder) |
stampHolder: 版本号容器 |
V |
获取引用和版本号 |
set(V newReference, int newStamp) |
newReference: 新引用newStamp: 新版本号 |
void |
设置引用和版本号 |
compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp) |
expectedReference: 期望引用newReference: 新引用expectedStamp: 期望版本号newStamp: 新版本号 |
boolean |
同时比较引用和版本号 |
attemptStamp(V expectedReference, int newStamp) |
expectedReference: 期望引用newStamp: 新版本号 |
boolean |
尝试只更新版本号 |
import java.util.concurrent.atomic.AtomicStampedReference;
/**
* AtomicStampedReference通过版本号解决ABA问题
* 每次修改都会增加版本号,确保不会误判
*/
public class AtomicStampedReferenceDemo {
public static void main(String[] args) {
String initialRef = "数据A";
int initialStamp = 0;
// 创建带版本号的原子引用
AtomicStampedReference<String> atomicStampedRef =
new AtomicStampedReference<>(initialRef, initialStamp);
System.out.println("初始引用: " + atomicStampedRef.getReference());
System.out.println("初始版本号: " + atomicStampedRef.getStamp());
// get(int[]): 同时获取引用和版本号
int[] stampHolder = new int[1];
String currentRef = atomicStampedRef.get(stampHolder);
System.out.println("当前引用: " + currentRef + ", 当前版本号: " + stampHolder[0]);
// 模拟ABA问题场景
String newRefB = "数据B";
String newRefA = "数据A"; // 又改回A,但版本号不同
// 第一次更新:A -> B,版本号 0 -> 1
boolean firstUpdate = atomicStampedRef.compareAndSet(
initialRef, newRefB, initialStamp, initialStamp + 1);
System.out.println("第一次更新(A->B)结果: " + firstUpdate);
System.out.println("当前引用: " + atomicStampedRef.getReference());
System.out.println("当前版本号: " + atomicStampedRef.getStamp());
// 第二次更新:B -> A,版本号 1 -> 2
boolean secondUpdate = atomicStampedRef.compareAndSet(
newRefB, newRefA, 1, 2);
System.out.println("第二次更新(B->A)结果: " + secondUpdate);
System.out.println("当前引用: " + atomicStampedRef.getReference());
System.out.println("当前版本号: " + atomicStampedRef.getStamp());
// 尝试用旧版本号更新(会失败)- 这就是解决ABA问题的关键!
boolean failedUpdate = atomicStampedRef.compareAndSet(
newRefA, "新数据", 0, 1); // 使用旧的版本号0
System.out.println("使用旧版本号更新结果: " + failedUpdate);
System.out.println("引用未被修改: " + atomicStampedRef.getReference());
// attemptStamp(): 只更新版本号
boolean stampUpdated = atomicStampedRef.attemptStamp(newRefA, 3);
System.out.println("版本号更新结果: " + stampUpdated);
System.out.println("新版本号: " + atomicStampedRef.getStamp());
// 正确的方式:使用当前版本号
stampHolder = new int[1];
currentRef = atomicStampedRef.get(stampHolder);
boolean correctUpdate = atomicStampedRef.compareAndSet(
currentRef, "最终数据", stampHolder[0], stampHolder[0] + 1);
System.out.println("使用正确版本号更新结果: " + correctUpdate);
System.out.println("最终引用: " + atomicStampedRef.getReference());
System.out.println("最终版本号: " + atomicStampedRef.getStamp());
}
}
ABA问题详解:
ABA问题是指:
- 线程1读取值A
- 线程2将值改为B,然后又改回A
- 线程1进行CAS操作,发现当前值仍是A,于是操作成功
虽然值看起来没变,但中间状态的变化可能对业务逻辑产生影响。AtomicStampedReference通过版本号完美解决了这个问题。
五、原子更新字段类
5.1 AtomicIntegerFieldUpdater - 原子更新整型字段
使用场景:优化内存使用、大量对象需要原子字段更新
核心API详解
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
newUpdater(Class<U> tclass, String fieldName) |
tclass: 目标类fieldName: 字段名 |
AtomicIntegerFieldUpdater<U> |
静态方法创建更新器 |
get(U obj) |
obj: 目标对象 |
int |
获取字段值 |
set(U obj, int newValue) |
obj: 目标对象newValue: 新值 |
void |
设置字段值 |
getAndSet(U obj, int newValue) |
obj: 目标对象newValue: 新值 |
int |
原子设置并返回旧值 |
compareAndSet(U obj, int expect, int update) |
obj: 目标对象expect: 期望值update: 更新值 |
boolean |
CAS操作 |
getAndIncrement(U obj) |
obj: 目标对象 |
int |
原子递增,返回旧值 |
getAndDecrement(U obj) |
obj: 目标对象 |
int |
原子递减,返回旧值 |
getAndAdd(U obj, int delta) |
obj: 目标对象delta: 增量 |
int |
原子加法,返回旧值 |
incrementAndGet(U obj) |
obj: 目标对象 |
int |
原子递增,返回新值 |
addAndGet(U obj, int delta) |
obj: 目标对象delta: 增量 |
int |
原子加法,返回新值 |
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
/**
* AtomicIntegerFieldUpdater以原子方式更新对象的volatile int字段
* 相比为每个对象创建AtomicInteger,可以节省大量内存
*/
public class AtomicIntegerFieldUpdaterDemo {
static class Counter {
// 必须用volatile修饰,保证可见性
public volatile int count;
private String name;
public Counter(String name, int initialCount) {
this.name = name;
this.count = initialCount;
}
public String getName() { return name; }
public int getCount() { return count; }
}
public static void main(String[] args) {
// 创建字段更新器,指定要更新的类和字段名
AtomicIntegerFieldUpdater<Counter> updater =
AtomicIntegerFieldUpdater.newUpdater(Counter.class, "count");
Counter counter1 = new Counter("计数器1", 0);
Counter counter2 = new Counter("计数器2", 10);
System.out.println("计数器1初始计数: " + counter1.getCount());
System.out.println("计数器2初始计数: " + counter2.getCount());
// get(): 获取字段值
System.out.println("通过updater获取计数器1的值: " + updater.get(counter1));
// set(): 设置字段值
updater.set(counter1, 5);
System.out.println("设置计数器1为5后的值: " + counter1.getCount());
// getAndIncrement(): 原子递增 - 相比synchronized性能更好
int oldCount = updater.getAndIncrement(counter1);
System.out.println("计数器1递增前值: " + oldCount + ", 当前值: " + counter1.getCount());
// getAndAdd(): 原子加法
oldCount = updater.getAndAdd(counter1, 10);
System.out.println("计数器1加10前值: " + oldCount + ", 当前值: " + counter1.getCount());
// incrementAndGet(): 原子递增并返回新值
int newCount = updater.incrementAndGet(counter1);
System.out.println("计数器1递增后的值: " + newCount);
// addAndGet(): 原子加法并返回新值
newCount = updater.addAndGet(counter1, 20);
System.out.println("计数器1加20后的值: " + newCount);
// compareAndSet(): 比较并设置
boolean updated = updater.compareAndSet(counter1, 36, 50);
System.out.println("计数器1 CAS操作结果: " + updated + ", 当前值: " + counter1.getCount());
// 可以同时更新多个对象的相同字段
updater.incrementAndGet(counter2);
System.out.println("计数器2递增后的值: " + counter2.getCount());
}
}
内存优化效果:
AtomicInteger对象:16-24字节 overheadvolatile int+AtomicIntegerFieldUpdater:4字节 + 静态updater- 当有大量对象时,内存节省效果显著
5.2 AtomicLongFieldUpdater - 原子更新长整型字段
使用场景:大数值字段的原子更新、内存敏感场景
核心API详解
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
newUpdater(Class<U> tclass, String fieldName) |
tclass: 目标类fieldName: 字段名 |
AtomicLongFieldUpdater<U> |
静态方法创建更新器 |
get(U obj) |
obj: 目标对象 |
long |
获取字段值 |
set(U obj, long newValue) |
obj: 目标对象newValue: 新值 |
void |
设置字段值 |
getAndSet(U obj, long newValue) |
obj: 目标对象newValue: 新值 |
long |
原子设置并返回旧值 |
compareAndSet(U obj, long expect, long update) |
obj: 目标对象expect: 期望值update: 更新值 |
boolean |
CAS操作 |
getAndAdd(U obj, long delta) |
obj: 目标对象delta: 增量 |
long |
原子加法,返回旧值 |
addAndGet(U obj, long delta) |
obj: 目标对象delta: 增量 |
long |
原子加法,返回新值 |
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
/**
* AtomicLongFieldUpdater用于原子更新long字段
* 适用于需要大数值范围且内存敏感的场景
*/
public class AtomicLongFieldUpdaterDemo {
static class Account {
// 必须用volatile修饰
public volatile long balance;
private final String owner;
public Account(String owner, long initialBalance) {
this.owner = owner;
this.balance = initialBalance;
}
public String getOwner() { return owner; }
public long getBalance() { return balance; }
}
public static void main(String[] args) {
AtomicLongFieldUpdater<Account> balanceUpdater =
AtomicLongFieldUpdater.newUpdater(Account.class, "balance");
Account account1 = new Account("张三", 1000L);
Account account2 = new Account("李四", 2000L);
System.out.println("张三账户初始余额: " + account1.getBalance());
System.out.println("李四账户初始余额: " + account2.getBalance());
// 基础操作
System.out.println("通过updater获取张三余额: " + balanceUpdater.get(account1));
balanceUpdater.set(account1, 1500L);
System.out.println("设置张三余额为1500后的值: " + account1.getBalance());
// 原子存款 - 无锁线程安全
balanceUpdater.addAndGet(account1, 500L);
System.out.println("张三存款500后余额: " + account1.getBalance());
// 原子取款
long oldBalance = balanceUpdater.getAndAdd(account1, -200L);
System.out.println("张三取款200前余额: " + oldBalance + ", 取款后余额: " + account1.getBalance());
// 比较并设置 - 实现转账等业务
boolean transferSuccess = balanceUpdater.compareAndSet(account1, 1800L, 2000L);
System.out.println("张三转账操作结果: " + transferSuccess + ", 当前余额: " + account1.getBalance());
// 同时操作多个账户
balanceUpdater.getAndAdd(account2, 1000L);
System.out.println("李四存款1000后余额: " + account2.getBalance());
}
}
5.3 AtomicReferenceFieldUpdater - 原子更新引用字段
使用场景:链表节点更新、树结构调整、对象关系维护
核心API详解
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
newUpdater(Class<U> tclass, Class<W> vclass, String fieldName) |
tclass: 目标类vclass: 字段类型fieldName: 字段名 |
AtomicReferenceFieldUpdater<U,W> |
静态方法创建更新器 |
get(U obj) |
obj: 目标对象 |
V |
获取字段引用 |
set(U obj, V newValue) |
obj: 目标对象newValue: 新引用 |
void |
设置字段引用 |
getAndSet(U obj, V newValue) |
obj: 目标对象newValue: 新引用 |
V |
原子设置并返回旧引用 |
compareAndSet(U obj, V expect, V update) |
obj: 目标对象expect: 期望引用update: 更新引用 |
boolean |
CAS操作 |
lazySet(U obj, V newValue) |
obj: 目标对象newValue: 新引用 |
void |
延迟设置引用 |
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
/**
* AtomicReferenceFieldUpdater用于原子更新引用字段
* 常用于实现无锁数据结构
*/
public class AtomicReferenceFieldUpdaterDemo {
static class Node<T> {
// 必须用volatile修饰
public volatile Node<T> next;
private final T value;
public Node(T value) {
this.value = value;
}
public T getValue() { return value; }
public Node<T> getNext() { return next; }
@Override
public String toString() {
return "Node{value=" + value + ", next=" + (next != null ? next.value : "null") + "}";
}
}
public static void main(String[] args) {
// 创建引用字段更新器
AtomicReferenceFieldUpdater<Node, Node> nextUpdater =
AtomicReferenceFieldUpdater.newUpdater(Node.class, Node.class, "next");
Node<String> first = new Node<>("第一个节点");
Node<String> second = new Node<>("第二个节点");
Node<String> third = new Node<>("第三个节点");
System.out.println("初始第一个节点的next: " + first.getNext());
// get(): 获取字段引用
System.out.println("通过updater获取第一个节点的next: " + nextUpdater.get(first));
// set(): 设置字段引用
nextUpdater.set(first, second);
System.out.println("设置第一个节点的next为第二个节点: " + first);
// compareAndSet(): 原子设置next字段 - 实现无锁链表
boolean setSuccess = nextUpdater.compareAndSet(first, second, third);
System.out.println("CAS操作结果: " + setSuccess);
System.out.println("第一个节点: " + first);
// getAndSet(): 获取并设置引用
Node<String> oldNext = nextUpdater.getAndSet(second, third);
System.out.println("第二个节点原来的next: " + oldNext);
System.out.println("第二个节点: " + second);
// lazySet(): 延迟设置
nextUpdater.lazySet(third, first); // 形成环状,仅作演示
System.out.println("第三个节点延迟设置后的next: " + third.getNext());
// 构建链表并展示
System.out.println("最终链表结构:");
Node<String> current = first;
int count = 0;
while (current != null && count < 5) { // 防止无限循环
System.out.println(current);
current = current.getNext();
count++;
}
}
}
六、综合实战:构建线程安全计数器
下面我们通过一个综合示例展示如何在实际项目中使用原子操作类:
import java.util.concurrent.atomic.*;
import java.util.concurrent.*;
/**
* 线程安全计数器综合示例
* 展示了多种原子类的实际应用
*/
public class ThreadSafeCounter {
// 基本计数器 - 使用AtomicInteger
private final AtomicInteger count = new AtomicInteger(0);
// 大数值统计 - 使用AtomicLong
private final AtomicLong total = new AtomicLong(0L);
// 状态控制 - 使用AtomicReference
private final AtomicReference<String> status = new AtomicReference<>("RUNNING");
// 统计数组 - 使用AtomicIntegerArray进行分桶统计
private final AtomicIntegerArray bucketStats = new AtomicIntegerArray(10);
// 配置信息 - 使用AtomicReference支持动态更新
private final AtomicReference<Config> config = new AtomicReference<>(new Config(100, 60));
// 标记位控制 - 使用AtomicBoolean
private final AtomicBoolean enabled = new AtomicBoolean(true);
static class Config {
final int maxConnections;
final int timeoutSeconds;
public Config(int maxConnections, int timeoutSeconds) {
this.maxConnections = maxConnections;
this.timeoutSeconds = timeoutSeconds;
}
@Override
public String toString() {
return "Config{maxConnections=" + maxConnections +
", timeoutSeconds=" + timeoutSeconds + "}";
}
}
// 核心API方法
public void increment() {
if (!enabled.get()) {
System.out.println("计数器已禁用,忽略操作");
return;
}
count.incrementAndGet();
total.addAndGet(1L);
// 分桶统计:根据count值决定放入哪个桶
int bucket = count.get() % 10;
bucketStats.getAndIncrement(bucket);
}
public void add(int value) {
if (!enabled.get()) {
System.out.println("计数器已禁用,忽略操作");
return;
}
count.addAndGet(value);
total.addAndGet(value);
}
public boolean setStatus(String expected, String newStatus) {
return status.compareAndSet(expected, newStatus);
}
public void updateConfig(Config newConfig) {
Config oldConfig;
do {
oldConfig = config.get();
System.out.println("尝试更新配置: " + oldConfig + " -> " + newConfig);
} while (!config.compareAndSet(oldConfig, newConfig));
System.out.println("配置更新成功");
}
public boolean enable() {
return enabled.compareAndSet(false, true);
}
public boolean disable() {
return enabled.compareAndSet(true, false);
}
// 获取统计信息
public void printStats() {
System.out.println("\n=== 统计信息 ===");
System.out.println("当前计数: " + count.get());
System.out.println("总数: " + total.get());
System.out.println("状态: " + status.get());
System.out.println("启用状态: " + enabled.get());
System.out.println("桶统计: " + bucketStats.toString());
Config currentConfig = config.get();
System.out.println("配置: " + currentConfig);
// 验证数据一致性
long sum = 0;
for (int i = 0; i < bucketStats.length(); i++) {
sum += bucketStats.get(i);
}
System.out.println("桶统计总和: " + sum + ", 计数: " + count.get() +
", 一致性: " + (sum == count.get()));
}
public static void main(String[] args) throws InterruptedException {
ThreadSafeCounter counter = new ThreadSafeCounter();
// 创建多个线程同时操作计数器
int threadCount = 10;
int operationsPerThread = 1000;
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
CountDownLatch latch = new CountDownLatch(threadCount);
System.out.println("开始并发测试...");
for (int i = 0; i < threadCount; i++) {
final int threadId = i;
executor.execute(() -> {
try {
for (int j = 0; j < operationsPerThread; j++) {
counter.increment();
// 每隔一定操作数更新配置
if (j % 200 == 0) {
counter.updateConfig(new Config(100 + j, 60));
}
// 模拟随机禁用/启用
if (j == 500 && threadId == 0) {
System.out.println("线程" + threadId + "尝试禁用计数器");
counter.disable();
Thread.sleep(10); // 短暂休眠
System.out.println("线程" + threadId + "尝试启用计数器");
counter.enable();
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
latch.countDown();
}
});
}
// 等待所有线程完成
latch.await();
executor.shutdown();
// 打印最终统计
counter.printStats();
int expectedCount = threadCount * operationsPerThread;
System.out.println("\n=== 测试结果 ===");
System.out.println("期望计数: " + expectedCount);
System.out.println("实际计数: " + counter.count.get());
System.out.println("计数正确: " + (counter.count.get() == expectedCount));
System.out.println("测试" + (counter.count.get() == expectedCount ? "通过" : "失败"));
}
}
七、原子操作类的工作原理
7.1 CAS机制详解
CAS(Compare-And-Swap)是原子操作类的核心,包含三个操作数:
- 内存位置(V)
- 期望原值(A)
- 新值(B)
CAS的语义是:"我认为V的值应该是A,如果是,那么将V的值更新为B,否则不修改并告诉我现在的值是多少"
CAS操作是硬件级别的原子操作,在现代CPU中通常通过以下方式实现:
- x86架构:
CMPXCHG指令 - ARM架构:
LDREX/STREX指令对
7.2 Unsafe类的作用
所有原子操作类底层都依赖sun.misc.Unsafe类,它提供了硬件级别的原子操作:
public final class Unsafe {
// 对象字段操作
public native long objectFieldOffset(Field f);
// 数组基础偏移
public native int arrayBaseOffset(Class arrayClass);
// 数组索引缩放
public native int arrayIndexScale(Class arrayClass);
// CAS操作
public final native boolean compareAndSwapObject(Object o, long offset,
Object expected, Object x);
public final native boolean compareAndSwapInt(Object o, long offset,
int expected, int x);
public final native boolean compareAndSwapLong(Object o, long offset,
long expected, long x);
// 获取和设置值
public native int getIntVolatile(Object o, long offset);
public native void putIntVolatile(Object o, long offset, int x);
// 延迟设置(有更弱的可见性保证)
public native void putOrderedInt(Object o, long offset, int x);
}
7.3 内存屏障与可见性
原子操作类通过内存屏障保证可见性:
- 写操作:在写入后插入写屏障,保证写入对其他线程可见
- 读操作:在读取前插入读屏障,保证读取到最新值
Java内存模型中的屏障类型:
- LoadLoad屏障:保证该屏障前的读操作先于屏障后的读操作完成
- StoreStore屏障:保证该屏障前的写操作先于屏障后的写操作完成
- LoadStore屏障:保证该屏障前的读操作先于屏障后的写操作完成
- StoreLoad屏障:保证该屏障前的所有写操作对其他处理器可见
八、性能对比与选型建议
8.1 性能对比
| 场景 | synchronized | 原子操作类 | 性能提升 |
|---|---|---|---|
| 低竞争 | 慢 | 快 | 2-10倍 |
| 中等竞争 | 中等 | 中等 | 相当 |
| 高竞争 | 快 | 慢(自旋) | 可能更差 |
8.2 不同原子类的性能特点
| 原子类 | 适用场景 | 性能特点 |
|---|---|---|
AtomicInteger |
普通计数器 | 性能优秀,适用大部分场景 |
AtomicLong |
大数值计数 | 在32位系统上性能较差 |
LongAdder |
高并发统计 | 高竞争环境下性能最优 |
AtomicReference |
对象引用更新 | 性能与对象大小相关 |
| 字段更新器 | 内存敏感场景 | 节省内存,性能稍差 |
8.3 选型指南
-
计数器场景
- 简单计数:
AtomicInteger - 大数值计数:
AtomicLong或LongAdder - 分桶统计:
AtomicIntegerArray
- 简单计数:
-
状态控制
- 布尔标志:
AtomicBoolean - 对象状态:
AtomicReference - 带版本状态:
AtomicStampedReference
- 布尔标志:
-
内存敏感场景
- 大量对象:字段更新器(
AtomicXXXFieldUpdater) - 缓存系统:
AtomicReference
- 大量对象:字段更新器(
-
数据结构
- 无锁队列:
AtomicReference - 无锁栈:
AtomicReference - 无锁链表:
AtomicReferenceFieldUpdater
- 无锁队列:
8.4 最佳实践
- 避免过度使用:不是所有场景都需要原子类
- 注意ABA问题:必要时使用带版本号的原子类
- 考虑高竞争:高竞争环境下考虑
LongAdder等替代方案 - 内存布局:字段更新器可以优化内存使用
- JDK8+特性:利用新的函数式更新方法
- 性能测试:在实际环境中进行性能测试
九、总结
Java原子操作类为我们提供了强大的无锁并发编程工具:
9.1 核心价值
- 13个原子类覆盖了基本类型、数组、引用和字段更新
- CAS机制基于硬件指令,性能优异
- 无锁设计避免了死锁和锁开销
- 丰富的API支持各种并发场景
9.2 使用场景总结
| 类别 | 主要类 | 核心用途 |
|---|---|---|
| 基本类型 | AtomicInteger, AtomicLong, AtomicBoolean |
计数器、状态标志 |
| 数组 | AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray |
并发数组、分桶统计 |
| 引用 | AtomicReference, AtomicStampedReference, AtomicMarkableReference |
对象缓存、状态管理 |
| 字段更新 | AtomicIntegerFieldUpdater, AtomicLongFieldUpdater, AtomicReferenceFieldUpdater |
内存优化、大量对象 |
9.3 学习建议
- 从简单开始:先掌握
AtomicInteger和AtomicReference - 理解原理:深入理解CAS机制和内存模型
- 实践应用:在真实项目中尝试使用原子类
- 性能调优:根据实际场景选择合适的原子类
- 持续学习:关注JDK新版本中的并发工具改进
掌握这些原子操作类,能够让我们在适当的场景下写出更高效、更安全的并发代码。记住,工具虽好,但要因地制宜,根据具体场景选择最合适的并发控制方案。
希望本文能帮助你深入理解Java原子操作类,在实际项目中游刃有余地处理并发问题!
进一步学习资源:
❤️ 如果你喜欢这篇文章,请点赞支持! 👍 同时欢迎关注我的博客,获取更多精彩内容!
本文来自博客园,作者:佛祖让我来巡山,转载请注明原文链接:https://www.cnblogs.com/sun-10387834/p/19172186

浙公网安备 33010602011771号