一、为什么需要原子操作类?
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字节 overhead
volatile 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();
}