原子操作详解
1. 原子类概述与分类
Java并发包java.util.concurrent.atomic
提供了一系列原子操作类,这些类被称为"18罗汉",它们基于CAS(Compare And Swap)无锁算法,提供了线程安全的原子操作。
1.1 18罗汉完整分类
graph TD
A[Java原子操作类18罗汉] --> B[基本类型原子类]
A --> C[数组类型原子类]
A --> D[引用类型原子类]
A --> E[对象属性修改原子类]
A --> F[原子操作增强类]
B --> B1[AtomicBoolean]
B --> B2[AtomicInteger]
B --> B3[AtomicLong]
C --> C1[AtomicIntegerArray]
C --> C2[AtomicLongArray]
C --> C3[AtomicReferenceArray]
D --> D1[AtomicReference]
D --> D2[AtomicStampedReference]
D --> D3[AtomicMarkableReference]
E --> E1[AtomicIntegerFieldUpdater]
E --> E2[AtomicLongFieldUpdater]
E --> E3[AtomicReferenceFieldUpdater]
F --> F1[LongAdder]
F --> F2[LongAccumulator]
F --> F3[DoubleAdder]
F --> F4[DoubleAccumulator]
1.2 完整列表
- AtomicBoolean - 布尔类型原子操作
- AtomicInteger - 整型原子操作
- AtomicLong - 长整型原子操作
- AtomicIntegerArray - 整型数组原子操作
- AtomicLongArray - 长整型数组原子操作
- AtomicReferenceArray - 引用类型数组原子操作
- AtomicIntegerFieldUpdater - 整型字段原子更新器
- AtomicLongFieldUpdater - 长整型字段原子更新器
- AtomicReference - 引用类型原子操作
- AtomicReferenceFieldUpdater - 引用类型字段原子更新器
- AtomicMarkableReference - 带标记的引用类型原子操作
- AtomicStampedReference - 带版本号的引用类型原子操作
- DoubleAccumulator - 双精度累加器
- DoubleAdder - 双精度加法器
- LongAccumulator - 长整型累加器
- LongAdder - 长整型加法器
2. 基本类型原子类
基本类型原子类包括AtomicBoolean
、AtomicInteger
和AtomicLong
,它们提供了对基本数据类型的线程安全操作。
2.1 常见API介绍
public final int get()
- 获取当前的值public final int getAndSet(int newValue)
- 获取当前的值,并设置新的值public final int getAndIncrement()
- 获取当前的值,并自增public final int getAndDecrement()
- 获取当前的值,并自减public final int getAndAdd(int delta)
- 获取当前的值,并加上预期的值boolean compareAndSet(int expect, int update)
- 如果输入的数值等于预期值,则以原子方式将该值设置为输入值
2.2 AtomicInteger实战案例
package com.juc.atomic;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicIntegerDemo {
public static final int SIZE = 50;
public static void main(String[] args) throws InterruptedException {
MyNumber myNumber = new MyNumber();
CountDownLatch countDownLatch = new CountDownLatch(SIZE);
for (int i = 0; i < SIZE; i++) {
new Thread(() -> {
try {
for (int j = 0; j < 1000; j++) {
myNumber.addPlusPlus();
}
} finally {
countDownLatch.countDown();
}
}, String.valueOf(i)).start();
}
// 保证线程的全部顺利计算完成
countDownLatch.await();
System.out.println(Thread.currentThread().getName() + "\t" + myNumber.atomicInteger.get());
}
}
class MyNumber {
AtomicInteger atomicInteger = new AtomicInteger(0);
public int addPlusPlus() {
return atomicInteger.getAndIncrement();
}
}
2.3 AtomicInteger源码分析
public class AtomicInteger extends Number implements java.io.Serializable {
// 获取Unsafe实例,用于直接操作内存
private static final Unsafe unsafe = Unsafe.getUnsafe();
// value字段在内存中的偏移量
private static final long valueOffset;
// 静态初始化,获取value字段的内存偏移
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
// 实际存储的值,使用volatile保证可见性
private volatile int value;
/**
* 构造函数,初始化值
*/
public AtomicInteger(int initialValue) {
value = initialValue;
}
/**
* 获取当前值
*/
public final int get() {
return value;
}
/**
* 设置新值
*/
public final void set(int newValue) {
value = newValue;
}
/**
* 原子性自增操作
*/
public final int getAndIncrement() {
// 调用Unsafe的getAndAddInt方法,增加1
return unsafe.getAndAddInt(this, valueOffset, 1);
}
/**
* CAS操作:比较并交换
* @param expect 期望值
* @param update 更新值
* @return 如果成功返回true,否则返回false
*/
public final boolean compareAndSet(int expect, int update) {
// 使用Unsafe的compareAndSwapInt方法进行CAS操作
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
}
3. 数组类型原子类
数组类型原子类包括AtomicIntegerArray
、AtomicLongArray
和AtomicReferenceArray
,它们提供了对数组元素的原子操作。
3.1 AtomicIntegerArray实战案例
package com.juc.atomic;
import java.util.concurrent.atomic.AtomicIntegerArray;
public class AtomicIntegerArrayDemo {
public static void main(String[] args) {
// 三种初始化方式
AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(new int[5]);
// AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(5);
// AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(new int[]{1, 2, 3, 4, 5});
// 打印初始值
for (int i = 0; i < atomicIntegerArray.length(); i++) {
System.out.println(atomicIntegerArray.get(i));
}
int tmpInt = 0;
// 设置下标为0的数据,值为1122
tmpInt = atomicIntegerArray.getAndSet(0, 1122);
System.out.println("tmpInt = " + tmpInt + "\t" + atomicIntegerArray.get(0));
// 对下标为0的元素进行自增操作
tmpInt = atomicIntegerArray.getAndIncrement(0);
System.out.println("tmpInt = " + tmpInt + "\t" + atomicIntegerArray.get(0));
}
}
3.2 AtomicIntegerArray源码分析
public class AtomicIntegerArray implements java.io.Serializable {
private static final Unsafe unsafe = Unsafe.getUnsafe();
// 数组中第一个元素的偏移量
private static final int base = unsafe.arrayBaseOffset(int[].class);
// 数组中元素的大小(以字节为单位)
private static final int shift;
// 实际存储数据的数组,使用final保证引用不变
private final int[] array;
static {
// 计算数组元素的偏移量
int scale = unsafe.arrayIndexScale(int[].class);
if ((scale & (scale - 1)) != 0)
throw new Error("data type scale not a power of two");
shift = 31 - Integer.numberOfLeadingZeros(scale);
}
/**
* 构造函数,创建指定长度的数组
*/
public AtomicIntegerArray(int length) {
array = new int[length];
}
/**
* 构造函数,使用现有数组初始化
*/
public AtomicIntegerArray(int[] array) {
this.array = array.clone(); // 克隆数组,避免外部修改
}
/**
* 计算数组元素的内存偏移量
*/
private long checkedByteOffset(int i) {
if (i < 0 || i >= array.length)
throw new IndexOutOfBoundsException("index " + i);
return byteOffset(i);
}
private static long byteOffset(int i) {
return ((long) i << shift) + base;
}
/**
* 获取指定位置的值
*/
public final int get(int i) {
return getRaw(checkedByteOffset(i));
}
private int getRaw(long offset) {
return unsafe.getIntVolatile(array, offset);
}
/**
* 原子性更新指定位置的值
*/
public final boolean compareAndSet(int i, int expect, int update) {
return compareAndSetRaw(checkedByteOffset(i), expect, update);
}
private boolean compareAndSetRaw(long offset, int expect, int update) {
return unsafe.compareAndSwapInt(array, offset, expect, update);
}
}
4. 引用类型原子类
引用类型原子类包括AtomicReference
、AtomicStampedReference
和AtomicMarkableReference
,它们提供了对引用类型的原子操作,并能解决ABA问题。
4.1 类型对比
graph TD
A[引用类型原子类] --> B[AtomicReference]
A --> C[AtomicStampedReference]
A --> D[AtomicMarkableReference]
B --> B1[基础引用原子操作]
B --> B2[可实现自旋锁]
B --> B3[存在ABA问题]
C --> C1[携带版本号的引用]
C --> C2[解决ABA问题]
C --> C3[记录修改次数]
D --> D1[带标记位的引用]
D --> D2[解决是否修改过]
D3[状态简化为true/false]
D --> D3
4.2 AtomicReference - 自旋锁实现
package com.juc.atomic;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
* 使用AtomicReference实现自旋锁
*/
public class SpinLockDemo {
// 原子引用线程
AtomicReference<Thread> atomicReference = new AtomicReference<>();
/**
* 加锁方法
*/
public void myLock() {
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName() + "\t come in");
// 自旋等待,直到成功将当前线程设置到atomicReference中
while (!atomicReference.compareAndSet(null, thread)) {
// 自旋等待
}
System.out.println(Thread.currentThread().getName() + "\t 获得锁");
}
/**
* 解锁方法
*/
public void myUnLock() {
Thread thread = Thread.currentThread();
atomicReference.compareAndSet(thread, null);
System.out.println(Thread.currentThread().getName() + "\t 释放锁");
}
}
// 测试类
class SpinLockTest {
public static void main(String[] args) {
SpinLockDemo spinLockDemo = new SpinLockDemo();
new Thread(() -> {
spinLockDemo.myLock();
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
spinLockDemo.myUnLock();
}, "A").start();
new Thread(() -> {
spinLockDemo.myLock();
spinLockDemo.myUnLock();
}, "B").start();
}
}
4.3 AtomicStampedReference - 解决ABA问题
package com.juc.atomic;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicStampedReference;
public class AtomicStampedReferenceDemo {
public static void main(String[] args) {
String initialRef = "Z3";
AtomicStampedReference<String> stampedReference =
new AtomicStampedReference<>(initialRef, 1);
// 线程1
new Thread(() -> {
int stamp = stampedReference.getStamp();
System.out.println("t1拿到的初始版本号: " + stamp);
// 暂停1秒,让t2线程拿到相同的版本号
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
// 期望版本号为1,更新为A1,版本号更新为2
boolean result = stampedReference.compareAndSet(initialRef, "A1", stamp, stamp + 1);
System.out.println("t1修改成功否: " + result + "\t当前实际最新值: " +
stampedReference.getReference() + "\t当前实际最新版本号: " + stampedReference.getStamp());
// 期望版本号为2,更新为A2,版本号更新为3
result = stampedReference.compareAndSet("A1", "A2",
stampedReference.getStamp(), stampedReference.getStamp() + 1);
System.out.println("t1修改成功否: " + result + "\t当前实际最新值: " +
stampedReference.getReference() + "\t当前实际最新版本号: " + stampedReference.getStamp());
}, "t1").start();
// 线程2
new Thread(() -> {
int stamp = stampedReference.getStamp();
System.out.println("t2拿到的初始版本号: " + stamp);
// 暂停3秒,保证t1线程完成一次ABA操作
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
// 此时版本号已经被t1修改,所以CAS操作会失败
boolean result = stampedReference.compareAndSet(initialRef, "B3", stamp, stamp + 1);
System.out.println("t2修改成功否: " + result + "\t当前实际最新值: " +
stampedReference.getReference() + "\t当前实际最新版本号: " + stampedReference.getStamp());
}, "t2").start();
}
}
4.4 AtomicMarkableReference - 一次性标记
package com.juc.atomic;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicMarkableReference;
public class AtomicMarkableReferenceDemo {
public static AtomicMarkableReference<Integer> markableReference =
new AtomicMarkableReference<>(100, false);
public static void main(String[] args) {
new Thread(() -> {
boolean marked = markableReference.isMarked();
System.out.println(Thread.currentThread().getName() + "\t默认标识:" + marked);
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
markableReference.compareAndSet(100, 1000, marked, !marked);
}, "t1").start();
new Thread(() -> {
boolean marked = markableReference.isMarked();
System.out.println(Thread.currentThread().getName() + "\t默认标识:" + marked);
try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
boolean result = markableReference.compareAndSet(100, 2000, marked, !marked);
System.out.println(Thread.currentThread().getName() + "\tt2线程CAS结果: " + result);
System.out.println(Thread.currentThread().getName() + "\t" + markableReference.getReference());
}, "t2").start();
}
}
5. 对象属性修改原子类
对象属性修改原子类包括AtomicIntegerFieldUpdater
、AtomicLongFieldUpdater
和AtomicReferenceFieldUpdater
,它们能够以线程安全的方式操作非线程安全对象内的某些字段。
5.1 使用要求
- 更新的对象属性必须使用
public volatile
修饰符 - 因为这些类都是抽象类,所以每次使用都必须使用静态方法
newUpdater()
创建一个更新器 - 需要设置想要更新的类和属性
5.2 AtomicIntegerFieldUpdater实战案例
package com.juc.atomic;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
class BankAccount {
String bankName = "CCB";
// 更新的对象属性必须使用public volatile修饰符
public volatile int money = 0;
// 传统synchronized方式
public synchronized void add() {
money++;
}
// 使用AtomicIntegerFieldUpdater
AtomicIntegerFieldUpdater<BankAccount> fieldUpdater =
AtomicIntegerFieldUpdater.newUpdater(BankAccount.class, "money");
// 不加synchronized,保证高性能原子性
public void transMoney(BankAccount bankAccount) {
fieldUpdater.getAndIncrement(bankAccount);
}
}
/**
* 以一种线程安全的方式操作非线程安全对象的某些字段
* 需求:10个线程,每个线程转账1000
*/
public class AtomicIntegerFieldUpdaterDemo {
public static void main(String[] args) throws InterruptedException {
BankAccount bankAccount = new BankAccount();
CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
bankAccount.transMoney(bankAccount);
}
countDownLatch.countDown();
}).start();
}
countDownLatch.await();
System.out.println(Thread.currentThread().getName() + " result: " + bankAccount.money);
}
}
5.3 AtomicReferenceFieldUpdater实战案例
package com.juc.atomic;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
class MyVar {
public volatile Boolean isInit = Boolean.FALSE;
AtomicReferenceFieldUpdater<MyVar, Boolean> referenceFieldUpdater =
AtomicReferenceFieldUpdater.newUpdater(MyVar.class, Boolean.class, "isInit");
public void init(MyVar myVar) throws InterruptedException {
if (referenceFieldUpdater.compareAndSet(myVar, Boolean.FALSE, Boolean.TRUE)) {
System.out.println(Thread.currentThread().getName() + " start init : need 2 seconds");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + " over init");
} else {
System.out.println(Thread.currentThread().getName() + " 已经完成初始化");
}
}
}
/**
* 需求:多线程并发调用一个类的初始化方法,如果未被初始化过,将执行初始化工作
* 要求只能被初始化一次,只有一个线程操作成功
*/
public class AtomicReferenceFieldUpdaterDemo {
public static void main(String[] args) {
MyVar myVar = new MyVar();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
myVar.init(myVar);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, String.valueOf(i)).start();
}
}
}
6. 原子操作增强类
Java 8引入了四个原子操作增强类:LongAdder
、LongAccumulator
、DoubleAdder
和DoubleAccumulator
。这些类在高并发场景下性能更优,特别是LongAdder
,被阿里巴巴开发手册推荐使用。
6.1 增强类性能对比
package com.juc.atomic;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;
class ClickNumber {
int number = 0;
// 传统synchronized方式
public synchronized void clickBysynchronized() {
number++;
}
// AtomicLong方式
AtomicLong atomicLong = new AtomicLong(0);
public void clickAtomicLong() {
atomicLong.getAndIncrement();
}
// LongAdder方式
LongAdder longAdder = new LongAdder();
public void clickLongAdder() {
longAdder.increment();
}
// LongAccumulator方式
LongAccumulator longAccumulator = new LongAccumulator((x, y) -> x + y, 0);
public void clickLongAccumulator() {
longAccumulator.accumulate(1);
}
}
/**
* 需求:50个线程,每个线程100W次,求总点赞数
*/
public class AccumulatorCompreDemo {
public static final int _1W = 10000;
public static final int threadNum = 50;
public static void main(String[] args) throws InterruptedException {
ClickNumber clickNumber = new ClickNumber();
long startTime;
long endTime;
CountDownLatch countDownLatch1 = new CountDownLatch(threadNum);
CountDownLatch countDownLatch2 = new CountDownLatch(threadNum);
CountDownLatch countDownLatch3 = new CountDownLatch(threadNum);
CountDownLatch countDownLatch4 = new CountDownLatch(threadNum);
// 测试synchronized
startTime = System.currentTimeMillis();
for (int i = 0; i < threadNum; i++) {
new Thread(() -> {
try {
for (int j = 0; j < 100 * _1W; j++) {
clickNumber.clickBysynchronized();
}
} finally {
countDownLatch1.countDown();
}
}, String.valueOf(i)).start();
}
countDownLatch1.await();
endTime = System.currentTimeMillis();
System.out.println("costTime: " + (endTime - startTime) + "毫秒 \t synchronized: " + clickNumber.number);
// 测试AtomicLong
startTime = System.currentTimeMillis();
for (int i = 0; i < threadNum; i++) {
new Thread(() -> {
try {
for (int j = 0; j < 100 * _1W; j++) {
clickNumber.clickAtomicLong();
}
} finally {
countDownLatch2.countDown();
}
}, String.valueOf(i)).start();
}
countDownLatch2.await();
endTime = System.currentTimeMillis();
System.out.println("costTime: " + (endTime - startTime) + "毫秒 \t AtomicLong: " + clickNumber.atomicLong.get());
// 测试LongAdder
startTime = System.currentTimeMillis();
for (int i = 0; i < threadNum; i++) {
new Thread(() -> {
try {
for (int j = 0; j < 100 * _1W; j++) {
clickNumber.clickLongAdder();
}
} finally {
countDownLatch3.countDown();
}
}, String.valueOf(i)).start();
}
countDownLatch3.await();
endTime = System.currentTimeMillis();
System.out.println("costTime: " + (endTime - startTime) + "毫秒 \t LongAdder: " + clickNumber.longAdder.sum());
// 测试LongAccumulator
startTime = System.currentTimeMillis();
for (int i = 0; i < threadNum; i++) {
new Thread(() -> {
try {
for (int j = 0; j < 100 * _1W; j++) {
clickNumber.clickLongAccumulator();
}
} finally {
countDownLatch4.countDown();
}
}, String.valueOf(i)).start();
}
countDownLatch4.await();
endTime = System.currentTimeMillis();
System.out.println("costTime: " + (endTime - startTime) + "毫秒 \t LongAccumulator: " + clickNumber.longAccumulator.get());
}
}
6.2 性能测试结果
costTime: 1756毫秒 synchronized: 50000000
costTime: 1084毫秒 AtomicLong: 50000000
costTime: 435毫秒 LongAdder: 50000000
costTime: 369毫秒 LongAccumulator: 50000000
可以看出,在高并发场景下,LongAdder和LongAccumulator的性能明显优于传统的synchronized和AtomicLong。
6.3 阿里巴巴开发手册推荐
阿里巴巴开发手册明确推荐:在JDK8中,推荐使用LongAdder对象,比AtomicLong性能更好(减少乐观锁的重试次数)。
7. LongAdder深度解析
7.1 LongAdder核心思想
LongAdder的基本思路就是分散热点,将value值分散到一个Cell数组中,不同线程会命中到数组的不同槽中,各个线程只对自己槽中的那个值进行CAS操作,这样热点就被分散了,冲突的概率就小很多。
7.2 数学表达式
LongAdder内部有一个base变量和一个Cell[]数组:
- base变量:低并发时,直接累加到该变量上
- Cell[]数组:高并发时,累加进各个线程自己的槽Cell[i]中
最终结果 = base + Cell[0] + Cell[1] + ... + Cell[n]
7.3 LongAdder源码解析
7.3.1 LongAdder内存模型
7.3.2 内存模型关键点说明
-
基础结构:
base
:volatile long类型,存储基础累加值cells
:volatile Cell数组,高并发时的分散存储cellsBusy
:volatile int类型,作为锁标志控制数组操作
-
Cell对象设计:
- 每个Cell包含一个volatile long类型的value
- 使用内存对齐填充避免伪共享(False Sharing)
- Cell大小通常为缓存行大小的倍数(64或128字节)
-
线程映射机制:
- 每个线程维护一个probe值(类似hash值)
- 通过
probe & (cells.length - 1)
定位到具体Cell - 不同线程尽量分散到不同Cell,减少竞争
-
内存屏障保证:
- 所有关键字段使用volatile保证可见性
- CAS操作提供原子性和内存屏障效果
7.3.3 add(1L)方法流程
/**
* LongAdder的add方法
*/
public void add(long x) {
Cell[] as; long b, v; int m; Cell a;
// 条件1:cells数组不为null,说明出现过竞争,需要使用cells数组
// 条件2:直接对base进行CAS操作失败,说明出现竞争
if ((as = cells) != null || !casBase(b = base, b + x)) {
boolean uncontended = true;
// 条件1:cells数组为null
// 条件2:cells数组长度为0
// 条件3:当前线程对应的cell为null
// 条件4:当前线程对应的cell CAS失败
if (as == null || (m = as.length - 1) < 0 ||
(a = as[getProbe() & m]) == null ||
!(uncontended = a.cas(v = a.value, v + x)))
// 进入longAccumulate方法处理
longAccumulate(x, null, uncontended);
}
}
7.3.2 longAccumulate核心逻辑
final void longAccumulate(long x, LongBinaryOperator fn, boolean wasUncontended) {
int h;
// 获取当前线程的probe值,用于定位Cell数组位置
if ((h = getProbe()) == 0) {
ThreadLocalRandom.current(); // 强制初始化
h = getProbe();
wasUncontended = true;
}
boolean collide = false; // 扩容意向:false表示不会扩容,true表示可能扩容
for (;;) { // 自旋
Cell[] as; Cell a; int n; long v;
// CASE1: Cell数组已经初始化
if ((as = cells) != null && (n = as.length) > 0) {
// 当前线程对应的Cell为null,需要创建新Cell
if ((a = as[(n - 1) & h]) == null) {
if (cellsBusy == 0) { // 尝试获取锁
Cell r = new Cell(x); // 创建新Cell
if (cellsBusy == 0 && casCellsBusy()) {
boolean created = false;
try {
Cell[] rs; int m, j;
if ((rs = cells) != null &&
(m = rs.length) > 0 &&
rs[j = (m - 1) & h] == null) {
rs[j] = r; // 将新Cell放入数组
created = true;
}
} finally {
cellsBusy = 0; // 释放锁
}
if (created)
break; // 成功创建Cell,跳出循环
continue; // 创建失败,继续自旋
}
}
collide = false;
}
// 当前线程CAS失败,重新计算probe值
else if (!wasUncontended)
wasUncontended = true;
// 尝试对当前Cell进行CAS操作
else if (a.cas(v = a.value, v + x))
break; // CAS成功,跳出循环
// 数组长度已达到CPU数量或数组已被其他线程修改
else if (n >= NCPU || cells != as)
collide = false; // 不能扩容
// 设置扩容意向
else if (!collide)
collide = true;
// 尝试扩容
else if (cellsBusy == 0 && casCellsBusy()) {
try {
if (cells == as) { // 检查数组是否被其他线程修改
Cell[] rs = new Cell[n << 1]; // 扩容为原来的2倍
for (int i = 0; i < n; ++i)
rs[i] = as[i]; // 复制原数组元素
cells = rs; // 更新数组引用
}
} finally {
cellsBusy = 0; // 释放锁
}
collide = false;
continue; // 扩容成功,继续自旋
}
h = advanceProbe(h); // 重新计算probe值
}
// CASE2: Cell数组未初始化,尝试初始化
else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
boolean init = false;
try {
if (cells == as) {
Cell[] rs = new Cell[2]; // 初始化为长度为2的数组
rs[h & 1] = new Cell(x); // 创建第一个Cell
cells = rs;
init = true;
}
} finally {
cellsBusy = 0; // 释放锁
}
if (init)
break; // 初始化成功,跳出循环
}
// CASE3: 兜底方案,尝试直接CAS操作base
else if (casBase(v = base, v + x))
break; // CAS成功,跳出循环
}
}
7.4 LongAdder工作流程图
graph TD
A[线程调用add方法] --> B{cells数组是否为null?}
B -->|是| C[尝试CAS操作base]
B -->|否| D[计算线程在cells数组中的位置]
C --> E{CAS操作base成功?}
E -->|是| F[操作完成]
E -->|否| G[进入longAccumulate]
D --> H{对应Cell是否为null?}
H -->|是| I[创建新Cell]
H -->|否| J[尝试CAS操作Cell]
J --> K{CAS操作成功?}
K -->|是| F
K -->|否| L[重新计算probe值]
I --> M{创建Cell成功?}
M -->|是| F
M -->|否| N[考虑扩容]
L --> O{需要扩容?}
O -->|是| P[扩容cells数组]
O -->|否| Q[重新自旋]
P --> Q
Q --> D
G --> R[longAccumulate处理]
R --> S[自旋直到成功]
S --> F
7.5 sum()方法分析
public long sum() {
Cell[] as = cells; Cell a;
long sum = base; // 从base开始累加
if (as != null) {
for (int i = 0; i < as.length; ++i) {
if ((a = as[i]) != null)
sum += a.value; // 累加每个Cell的值
}
}
return sum;
}
7.6 为什么LongAdder在并发情况下sum的值不精确?
重要提醒:sum执行时,并没有限制对base和cells的更新。所以LongAdder不是强一致性的,它是最终一致性的。
graph LR
A[sum方法开始] --> B[读取base值]
B --> C[遍历cells数组]
C --> D[累加cell值]
D --> E[返回总和]
F[其他线程] --> G[修改base]
F --> H[修改cells]
G -.-> B
H -.-> D
I[问题] --> J[读取base后base被修改]
I --> K[读取cell时cell被修改]
I --> L[结果可能不准确]
导致不精确的原因:
- base值不一致:sum执行时,base被复制到局部变量,但在返回前base可能已经被其他线程更新
- Cell读取不一致:对Cell的读取也无法保证是最后一次写入的值
- 没有加锁保护:sum方法没有使用任何同步机制来保证一致性
因此,sum方法只能在没有并发更新的情况下获得正确结果,在高并发场景下只能作为一个近似值参考。
8. 总结
8.1 原子类选择指南
graph TD
A[选择合适的原子类] --> B{操作对象类型}
B -->|基本类型| C[AtomicInteger/AtomicLong/AtomicBoolean]
B -->|数组| D[AtomicIntegerArray/AtomicLongArray/AtomicReferenceArray]
B -->|引用对象| E[AtomicReference系列]
B -->|对象字段| F[FieldUpdater系列]
B -->|高并发累加| G[LongAdder/LongAccumulator]
E --> H{是否需要解决ABA问题}
H -->|是| I[AtomicStampedReference]
H -->|否| J[AtomicReference]
I --> K{需要记录修改次数?}
K -->|是| L[AtomicStampedReference]
K -->|否| M[AtomicMarkableReference]
G --> N{并发度}
N -->|高并发| O[LongAdder - 推荐]
N -->|低并发| P[AtomicLong]
8.2 性能对比总结
原子类型 | 适用场景 | 性能特点 | 一致性 |
---|---|---|---|
AtomicInteger/Long | 低并发场景 | 中等 | 强一致性 |
synchronized | 任何场景 | 较低 | 强一致性 |
LongAdder | 高并发累加 | 最高 | 最终一致性 |
LongAccumulator | 高并发自定义操作 | 最高 | 最终一致性 |
8.3 核心原理总结
- CAS无锁算法:所有原子类都基于CAS操作,避免了锁的开销
- 分散热点:LongAdder通过Cell数组分散竞争,提高并发性能
- 内存可见性:volatile关键字保证了修改的可见性
- ABA问题解决:通过版本号或标记位机制解决ABA问题
8.4 最佳实践建议
- JDK8+推荐使用LongAdder:在高并发累加场景下,LongAdder性能远超AtomicLong
- 根据场景选择:不是所有场景都适合LongAdder,低并发时AtomicLong更简单
- 注意一致性要求:如果需要强一致性,避免使用LongAdder的sum()方法
- 合理使用FieldUpdater:对于已有类的字段更新,FieldUpdater是很好的选择
- 解决ABA问题:在需要的场景下,使用AtomicStampedReference或AtomicMarkableReference
8.5 阿里巴巴规约建议
根据阿里巴巴开发手册推荐:
在JDK8中,推荐使用LongAdder对象,比AtomicLong性能更好(减少乐观锁的重试次数)。
这个建议的核心原因就是LongAdder通过分散热点的设计思想,在高并发场景下显著减少了CAS操作的冲突,从而提升了整体性能。
总结:Java原子操作类提供了丰富的线程安全解决方案,从基本的AtomicInteger到高性能的LongAdder,每种都有其适用的场景。理解它们的工作原理和适用场景,能帮助我们在实际开发中做出更好的选择,编写出更高性能的并发程序。