Java并发面试题 - 你使用过Java中的哪些原子类?
引言
在Java并发编程中,原子类是一组非常重要的工具,它们提供了一种无锁的线程安全操作方式。本文将介绍Java中常用的原子类及其使用场景,并通过流程图帮助理解其工作原理。
1. 原子类概述
Java中的原子类位于java.util.concurrent.atomic
包下,它们利用CAS(Compare-And-Swap)操作实现了非阻塞的线程安全操作,相比传统的synchronized
关键字,提供了更好的性能。
2. 基本类型原子类
2.1 AtomicInteger
用于原子操作的整型变量:
AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet(); // 原子递增
counter.getAndAdd(5); // 先获取当前值再加5
2.2 AtomicLong
用于原子操作的长整型变量:
AtomicLong bigCounter = new AtomicLong(0L);
bigCounter.compareAndSet(0, 100); // 如果当前值为0则设置为100
2.3 AtomicBoolean
用于原子操作的布尔变量:
AtomicBoolean flag = new AtomicBoolean(true);
flag.compareAndSet(true, false); // 如果当前为true则设置为false
3. 引用类型原子类
3.1 AtomicReference
用于原子操作的引用类型:
AtomicReference<String> ref = new AtomicReference<>("initial");
ref.compareAndSet("initial", "updated");
3.2 AtomicStampedReference
解决ABA问题的引用类型,带有版本号:
AtomicStampedReference<String> stampedRef =
new AtomicStampedReference<>("value", 0);
int[] stampHolder = new int[1];
String oldValue = stampedRef.get(stampHolder);
stampedRef.compareAndSet(oldValue, "newValue", stampHolder[0], stampHolder[0]+1);
3.3 AtomicMarkableReference
与AtomicStampedReference类似,但使用布尔标记而非版本号:
AtomicMarkableReference<String> markableRef =
new AtomicMarkableReference<>("value", false);
boolean[] markHolder = new boolean[1];
String current = markableRef.get(markHolder);
markableRef.compareAndSet(current, "newValue", markHolder[0], !markHolder[0]);
4. 数组原子类
4.1 AtomicIntegerArray
原子操作的整型数组:
AtomicIntegerArray array = new AtomicIntegerArray(10);
array.incrementAndGet(0); // 原子递增第0个元素
4.2 AtomicLongArray
原子操作的长整型数组:
AtomicLongArray longArray = new AtomicLongArray(10);
longArray.addAndGet(0, 100L); // 原子增加第0个元素100
4.3 AtomicReferenceArray
原子操作的引用类型数组:
AtomicReferenceArray<String> refArray = new AtomicReferenceArray<>(10);
refArray.compareAndSet(0, null, "new value");
5. 字段更新器
5.1 AtomicIntegerFieldUpdater
用于原子更新对象的整型字段:
class MyClass {
volatile int count;
}
AtomicIntegerFieldUpdater<MyClass> updater =
AtomicIntegerFieldUpdater.newUpdater(MyClass.class, "count");
MyClass obj = new MyClass();
updater.incrementAndGet(obj);
5.2 AtomicLongFieldUpdater
用于原子更新对象的长整型字段:
AtomicLongFieldUpdater<MyClass> longUpdater =
AtomicLongFieldUpdater.newUpdater(MyClass.class, "longField");
5.3 AtomicReferenceFieldUpdater
用于原子更新对象的引用类型字段:
AtomicReferenceFieldUpdater<MyClass, String> refUpdater =
AtomicReferenceFieldUpdater.newUpdater(MyClass.class, String.class, "refField");
6. 累加器类(Java 8+)
6.1 LongAdder
高并发下比AtomicLong性能更好:
LongAdder adder = new LongAdder();
adder.add(10);
long sum = adder.sum();
6.2 DoubleAdder
用于double类型的累加:
DoubleAdder doubleAdder = new DoubleAdder();
doubleAdder.add(1.5);
double sum = doubleAdder.sum();
6.3 LongAccumulator
更通用的累加器,可以自定义操作:
LongAccumulator accumulator = new LongAccumulator(Long::max, Long.MIN_VALUE);
accumulator.accumulate(100);
accumulator.accumulate(50);
long max = accumulator.get();
7. 使用场景对比
场景 | 推荐类 | 原因 |
---|---|---|
简单计数器 | AtomicInteger/AtomicLong | 简单易用 |
高并发计数器 | LongAdder/DoubleAdder | 性能更好 |
对象引用更新 | AtomicReference | 通用引用更新 |
需要解决ABA问题 | AtomicStampedReference | 带版本控制 |
数组元素原子操作 | AtomicXxxArray | 数组专用 |
已有类字段原子更新 | AtomicXxxFieldUpdater | 无需修改类结构 |
8. 总结
Java的原子类提供了一种高效的线程安全操作方式,避免了锁带来的性能开销。选择合适的原子类需要根据具体场景:
- 基本类型操作选择对应的AtomicXxx
- 高并发计数选择LongAdder/DoubleAdder
- 引用类型操作选择AtomicReference及其变种
- 数组操作选择AtomicXxxArray
- 已有类字段更新选择AtomicXxxFieldUpdater
通过合理使用这些原子类,可以显著提高Java并发程序的性能和可靠性。