1. Java原子操作类概述
在多线程编程中,保证数据操作的原子性是确保线程安全的关键。Java从JDK 1.5开始,在java.util.concurrent.atomic包下提供了一系列原子操作类,这些类能够在不使用锁的情况下实现线程安全的操作。原子操作类的底层实现主要依赖于CAS(Compare-And-Swap)机制和volatile关键字。
原子操作类主要分为以下四类:
- 基本类型原子类:AtomicBoolean、AtomicInteger、AtomicLong
- 数组类型原子类:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
- 引用类型原子类:AtomicReference、AtomicStampedReference、AtomicMarkableReference
- 字段更新原子类:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater
2. 基本类型原子类详解
2.1 AtomicInteger原理与使用
AtomicInteger是最常用的原子类之一,它提供了原子性的int值操作。其核心实现依赖于Unsafe类的CAS操作:
java复制public class AtomicInteger extends Number implements java.io.Serializable {
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
}
典型使用场景:
- 计数器:多线程环境下的计数操作
- 序列号生成:保证序列号的唯一性和递增性
- 状态标志:替代volatile boolean的复杂状态管理
注意:虽然AtomicInteger性能优于synchronized,但在高并发场景下频繁的CAS失败会导致CPU资源浪费,此时应考虑LongAdder。
2.2 AtomicBoolean实现分析
AtomicBoolean内部使用int值存储状态(1表示true,0表示false),其核心方法compareAndSet实现如下:
java复制public final boolean compareAndSet(boolean expect, boolean update) {
int e = expect ? 1 : 0;
int u = update ? 1 : 0;
return unsafe.compareAndSwapInt(this, valueOffset, e, u);
}
使用示例:
java复制AtomicBoolean flag = new AtomicBoolean(false);
// 保证只有一个线程能成功修改为true
if(flag.compareAndSet(false, true)) {
// 执行初始化操作
}
2.3 AtomicLong的特殊考量
AtomicLong在32位系统上存在伪共享问题,因为long是64位的,在32位系统中需要分成两次操作。解决方案:
- 使用@Contended注解(JDK8+)
- 对于统计场景,优先使用LongAdder
性能对比:
- 低竞争:AtomicLong ≈ LongAdder
- 高竞争:LongAdder性能显著优于AtomicLong
3. 数组原子类深度解析
3.1 AtomicIntegerArray实现机制
AtomicIntegerArray并不是简单地对数组元素进行原子操作,而是在内部维护了一个final的int数组,所有操作都通过Unsafe类实现:
java复制public class AtomicIntegerArray implements java.io.Serializable {
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final int base = unsafe.arrayBaseOffset(int[].class);
private final int[] array;
public final int getAndIncrement(int i) {
return unsafe.getAndAddInt(array, checkedByteOffset(i), 1);
}
}
典型应用场景:
- 并发统计:如统计不同分类的访问量
- 环形缓冲区:多生产者/消费者模型
3.2 引用类型数组的特殊处理
AtomicReferenceArray可以原子化地更新对象引用数组的元素,使用时需要注意:
- 数组长度在构造时确定且不可变
- 元素可以为null
- compareAndSet操作比较的是引用地址,不是对象内容
java复制AtomicReferenceArray<String> refArray = new AtomicReferenceArray<>(10);
refArray.compareAndSet(0, null, "initialValue");
4. 引用类型原子类应用
4.1 AtomicReference的ABA问题
标准AtomicReference无法解决ABA问题,示例:
java复制AtomicReference<String> ref = new AtomicReference<>("A");
// 线程1:A -> B -> A
// 线程2:此时仍然能成功执行CAS
ref.compareAndSet("A", "C");
解决方案:
- 使用AtomicStampedReference添加版本号
- 使用AtomicMarkableReference添加标记位
4.2 AtomicStampedReference实现原理
通过Pair内部类维护引用和版本戳:
java复制public class AtomicStampedReference<V> {
private static class Pair<T> {
final T reference;
final int stamp;
private Pair(T reference, int stamp) {
this.reference = reference;
this.stamp = stamp;
}
}
public boolean compareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
Pair<V> current = pair;
return expectedReference == current.reference &&
expectedStamp == current.stamp &&
((newReference == current.reference &&
newStamp == current.stamp) ||
casPair(current, Pair.of(newReference, newStamp)));
}
}
4.3 引用类型原子类的性能优化
- 避免频繁创建新对象
- 对于不变对象,可以直接替换引用
- 考虑使用享元模式减少对象创建
5. 字段更新原子类实战
5.1 AtomicIntegerFieldUpdater使用规范
字段更新器允许对指定类的volatile字段进行原子更新,使用要求:
- 字段必须是volatile修饰
- 必须是实例字段(不能是static)
- 字段必须对Updater可见
java复制class MyClass {
volatile int value;
}
AtomicIntegerFieldUpdater<MyClass> updater =
AtomicIntegerFieldUpdater.newUpdater(MyClass.class, "value");
MyClass obj = new MyClass();
updater.compareAndSet(obj, 0, 1);
5.2 字段更新器的性能优势
相比AtomicReference:
- 内存占用更小(不需要包装对象)
- 可以直接操作现有对象的字段
- 适合大规模对象池的场景
6. JDK8新增原子类
6.1 LongAdder设计思想
LongAdder采用分段累加策略解决高竞争下的性能问题:
- 基础值base:低竞争时直接CAS更新
- Cell数组:高竞争时分散热点
java复制public void increment() {
Cell[] as; long b, v; int m; Cell a;
if ((as = cells) != null ||
!casBase(b = base, b + 1)) {
boolean uncontended = true;
if (as == null || (m = as.length - 1) < 0 ||
(a = as[getProbe() & m]) == null ||
!(uncontended = a.cas(v = a.value, v + 1)))
longAccumulate(1L, null, uncontended);
}
}
6.2 DoubleAdder的精度处理
DoubleAdder内部通过LongAdder实现,使用Double.doubleToRawLongBits和Double.longBitsToDouble进行转换,需要注意:
- 不是严格的原子操作
- 可能存在精度损失
- 适合统计场景,不适用于金融计算
7. 原子操作类性能优化
7.1 伪共享问题解决方案
- @Contended注解(需要开启JVM参数-XX:-RestrictContended)
- 手动填充(适用于JDK7及以下版本)
java复制class PaddedAtomicLong extends AtomicLong {
public volatile long p1, p2, p3, p4, p5, p6 = 7L;
// 实际值由父类的value字段保存
}
7.2 自旋策略优化
- 适当增加自旋次数(针对短时竞争)
- 配合Thread.yield()或LockSupport.parkNanos()
- 考虑使用自适应自旋算法
8. 原子类在并发框架中的应用
8.1 ConcurrentHashMap的实现
ConcurrentHashMap使用原子操作实现无锁读和分段锁写:
- sizeCtl使用AtomicInteger控制初始化
- Node的next字段使用volatile修饰
- putVal使用synchronized和CAS结合
8.2 AQS中的状态管理
AbstractQueuedSynchronizer使用AtomicInteger维护同步状态:
java复制private volatile int state;
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
9. 原子操作类最佳实践
- 简单计数器优先使用AtomicInteger/Long
- 高并发统计使用LongAdder/DoubleAdder
- 对象引用更新注意ABA问题
- 字段更新器适合大规模对象池
- 避免过度依赖原子类,复杂操作考虑使用锁
10. 常见问题排查
10.1 CAS失败频繁
表现:CPU占用高但吞吐量低
解决方案:
- 减少竞争(数据分片)
- 改用LongAdder
- 增加补偿机制
10.2 内存可见性问题
即使使用原子类,复合操作仍需同步:
java复制// 错误示例
if(atomicInt.get() < 10) {
// 这里可能已经被其他线程修改
atomicInt.incrementAndGet();
}
// 正确做法
int oldVal;
do {
oldVal = atomicInt.get();
if(oldVal >= 10) break;
} while(!atomicInt.compareAndSet(oldVal, oldVal+1));
11. JVM层实现原理
11.1 Unsafe类的作用
Unsafe提供以下关键功能:
- 直接内存访问
- CAS操作
- 内存屏障
- 对象字段偏移量获取
11.2 内存屏障类型
原子类使用的内存屏障:
- LoadLoad屏障
- StoreStore屏障
- LoadStore屏障
- StoreLoad屏障(最重)
12. 原子类与锁的性能对比
测试场景:100个线程,每个执行10000次递增
| 实现方式 | 耗时(ms) | CPU使用率 |
|---|---|---|
| synchronized | 450 | 90% |
| ReentrantLock | 380 | 85% |
| AtomicInteger | 120 | 60% |
| LongAdder | 80 | 40% |
13. 扩展应用场景
13.1 无锁数据结构实现
基于原子类可以实现:
- 无锁队列(Michael-Scott算法)
- 无锁栈(Treiber算法)
- 无锁哈希表
13.2 状态机实现
使用AtomicReference实现线程安全状态机:
java复制enum State { INIT, PROCESSING, DONE }
AtomicReference<State> state = new AtomicReference<>(State.INIT);
void process() {
if(state.compareAndSet(State.INIT, State.PROCESSING)) {
try {
// 处理逻辑
state.set(State.DONE);
} catch(Exception e) {
state.set(State.INIT);
}
}
}
在实际项目中,合理选择原子操作类可以显著提升并发性能,但需要根据具体场景权衡一致性和性能需求。对于复杂的业务逻辑,有时简单的锁反而能提供更清晰的安全保证。
