Java并发之原子操作类18罗汉神通倍增-LongAdder解读
2023-10-08 06:30:27
LongAdder:Java并发编程中的神兵利器
什么是原子操作?
在多线程编程中,原子操作指的是在多线程环境下对共享资源进行安全且一致的访问。Java并发包提供了丰富的原子操作类,其中AtomicLong、AtomicInteger等基本类型原子类广为人知。
原子操作类的局限性
然而,随着并发场景的复杂化,基本类型原子操作类也暴露了一些局限性:
- 性能瓶颈: 基本类型原子类采用锁机制保证原子性,当并发量较大时,锁竞争会成为性能瓶颈。
- 拓展性差: 基本类型原子类仅支持基本数据类型,对于复杂对象或引用类型,无法提供原子操作支持。
- 功能单一: 基本类型原子类只能实现简单的原子增减或更新操作,对于更复杂的原子操作需求,难以满足。
LongAdder:原子操作的革命
为了解决这些局限,Java并发包引入了原子操作增强类,其中LongAdder是引用类型原子类的杰出代表。LongAdder通过分段锁机制和可扩展性设计,极大地提高了并发性能和适应性。
分段锁机制
LongAdder将Long值拆分成多个片段(segment),每个片段使用独立的锁进行保护。这种分段锁机制有效降低了锁竞争,从而提升了并发性能。
可扩展性
LongAdder可以自动扩容片段数量,适应并发量的变化。当片段数量超过阈值时,LongAdder会创建新的片段并重新分配数据,确保高并发场景下的稳定运行。
支持复杂原子操作
除了基本的增减操作,LongAdder还支持更复杂的原子操作,如:
- 累加数组元素
- 获取最大值或最小值
- 统计非负整数出现的次数
LongAdder的应用场景
LongAdder广泛应用于需要高并发且低延迟的场景,如:
- 计数器
- 统计信息收集
- 并行归约
LongAdder的源码解析
核心成员变量
cells
:分段数组,存储Long值。threshold
:分段数量的阈值,当超过阈值时,LongAdder会自动扩容。
构造方法
public LongAdder() {
this(Runtime.getRuntime().availableProcessors());
}
默认构造方法根据CPU核数创建分段数组。
increment操作
public void increment() {
Cell[] as = cells;
int m = (int) (getProbe() & (as.length - 1));
Cell a;
while ((a = as[m]) == null) {
Cell r = new Cell(0L, null);
if (a == null) {
as[m] = r;
break;
}
if (m == 0) {
threshold = Math.min(as.length * 2, MAXIMUM_CELLS);
Cell[] as2 = new Cell[threshold];
System.arraycopy(as, 0, as2, 0, as.length);
cells = as2;
as = as2;
}
}
a.value += 1L;
}
increment操作通过分段锁机制,尝试获取分段并累加Long值。如果分段不存在,则创建新分段并竞态设置。当分段数量超过阈值时,LongAdder会扩容分段数组。
decrement操作
public void decrement() {
Cell[] as = cells;
int m = (int) (getProbe() & (as.length - 1));
Cell a;
while ((a = as[m]) == null) {
Cell r = new Cell(0L, null);
if (a == null) {
as[m] = r;
break;
}
if (m == 0) {
threshold = Math.min(as.length * 2, MAXIMUM_CELLS);
Cell[] as2 = new Cell[threshold];
System.arraycopy(as, 0, as2, 0, as.length);
cells = as2;
as = as2;
}
}
a.value -= 1L;
}
decrement操作与increment操作类似,但进行的是减法操作。
结论
LongAdder通过分段锁机制和可扩展性设计,极大地提升了并发性能和适应性。其源码解析展示了其内部实现的精巧与高效,为并发编程提供了强大的工具。掌握LongAdder的使用技巧,将助你