JUC并发编程 原子操作详解

20 阅读14分钟

原子操作详解

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 完整列表

  1. AtomicBoolean - 布尔类型原子操作
  2. AtomicInteger - 整型原子操作
  3. AtomicLong - 长整型原子操作
  4. AtomicIntegerArray - 整型数组原子操作
  5. AtomicLongArray - 长整型数组原子操作
  6. AtomicReferenceArray - 引用类型数组原子操作
  7. AtomicIntegerFieldUpdater - 整型字段原子更新器
  8. AtomicLongFieldUpdater - 长整型字段原子更新器
  9. AtomicReference - 引用类型原子操作
  10. AtomicReferenceFieldUpdater - 引用类型字段原子更新器
  11. AtomicMarkableReference - 带标记的引用类型原子操作
  12. AtomicStampedReference - 带版本号的引用类型原子操作
  13. DoubleAccumulator - 双精度累加器
  14. DoubleAdder - 双精度加法器
  15. LongAccumulator - 长整型累加器
  16. LongAdder - 长整型加法器

2. 基本类型原子类

基本类型原子类包括AtomicBooleanAtomicIntegerAtomicLong,它们提供了对基本数据类型的线程安全操作。

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. 数组类型原子类

数组类型原子类包括AtomicIntegerArrayAtomicLongArrayAtomicReferenceArray,它们提供了对数组元素的原子操作。

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. 引用类型原子类

引用类型原子类包括AtomicReferenceAtomicStampedReferenceAtomicMarkableReference,它们提供了对引用类型的原子操作,并能解决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. 对象属性修改原子类

对象属性修改原子类包括AtomicIntegerFieldUpdaterAtomicLongFieldUpdaterAtomicReferenceFieldUpdater,它们能够以线程安全的方式操作非线程安全对象内的某些字段。

5.1 使用要求

  1. 更新的对象属性必须使用public volatile修饰符
  2. 因为这些类都是抽象类,所以每次使用都必须使用静态方法newUpdater()创建一个更新器
  3. 需要设置想要更新的类和属性

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引入了四个原子操作增强类:LongAdderLongAccumulatorDoubleAdderDoubleAccumulator。这些类在高并发场景下性能更优,特别是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内存模型

12121212.png

7.3.2 内存模型关键点说明
  1. 基础结构

    • base:volatile long类型,存储基础累加值
    • cells:volatile Cell数组,高并发时的分散存储
    • cellsBusy:volatile int类型,作为锁标志控制数组操作
  2. Cell对象设计

    • 每个Cell包含一个volatile long类型的value
    • 使用内存对齐填充避免伪共享(False Sharing)
    • Cell大小通常为缓存行大小的倍数(64或128字节)
  3. 线程映射机制

    • 每个线程维护一个probe值(类似hash值)
    • 通过probe & (cells.length - 1)定位到具体Cell
    • 不同线程尽量分散到不同Cell,减少竞争
  4. 内存屏障保证

    • 所有关键字段使用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[结果可能不准确]

导致不精确的原因

  1. base值不一致:sum执行时,base被复制到局部变量,但在返回前base可能已经被其他线程更新
  2. Cell读取不一致:对Cell的读取也无法保证是最后一次写入的值
  3. 没有加锁保护: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 核心原理总结

  1. CAS无锁算法:所有原子类都基于CAS操作,避免了锁的开销
  2. 分散热点:LongAdder通过Cell数组分散竞争,提高并发性能
  3. 内存可见性:volatile关键字保证了修改的可见性
  4. ABA问题解决:通过版本号或标记位机制解决ABA问题

8.4 最佳实践建议

  1. JDK8+推荐使用LongAdder:在高并发累加场景下,LongAdder性能远超AtomicLong
  2. 根据场景选择:不是所有场景都适合LongAdder,低并发时AtomicLong更简单
  3. 注意一致性要求:如果需要强一致性,避免使用LongAdder的sum()方法
  4. 合理使用FieldUpdater:对于已有类的字段更新,FieldUpdater是很好的选择
  5. 解决ABA问题:在需要的场景下,使用AtomicStampedReference或AtomicMarkableReference

8.5 阿里巴巴规约建议

根据阿里巴巴开发手册推荐:

在JDK8中,推荐使用LongAdder对象,比AtomicLong性能更好(减少乐观锁的重试次数)。

这个建议的核心原因就是LongAdder通过分散热点的设计思想,在高并发场景下显著减少了CAS操作的冲突,从而提升了整体性能。


总结:Java原子操作类提供了丰富的线程安全解决方案,从基本的AtomicInteger到高性能的LongAdder,每种都有其适用的场景。理解它们的工作原理和适用场景,能帮助我们在实际开发中做出更好的选择,编写出更高性能的并发程序。