Java并发编程利器:深入解析13个原子操作类

在多线程并发环境下,保证数据操作的原子性是个常见且关键的挑战。Java从JDK 1.5开始提供了java.util.concurrent.atomic包,其中包含13个强大的原子操作类,让我们能够以无锁的方式实现线程安全。本文将带你深入理解这些原子类的原理、API和使用场景。

一、为什么需要原子操作类?

1.1 问题的由来

想象一下这样的场景:多个线程同时操作同一个银行账户进行取款,如果不加控制,可能会出现什么情况?

// 不安全的计数器示例
class UnsafeCounter {
    private int count = 0;
    
    public void increment() {
        count++; // 这不是原子操作!
    }
}

count++看似简单,实际上包含三个步骤:

  1. 读取count的当前值
  2. 将值加1
  3. 将新值写回count

在多线程环境下,这两个步骤可能被其他线程打断,导致数据不一致。

1.2 传统的解决方案及其缺点

传统做法是使用synchronized关键字:

class SynchronizedCounter {
    private int count = 0;
    
    public synchronized void increment() {
        count++;
    }
}

synchronized确实能保证线程安全,但存在以下问题:

  • 性能开销:锁的获取和释放需要代价
  • 可能死锁:不正确的锁顺序可能导致死锁
  • 降低并发性:同一时刻只有一个线程能访问

1.3 原子操作类的优势

原子操作类基于CAS(Compare-And-Swap) 机制,提供了:

  • 无锁编程:避免传统锁的开销
  • 高性能:在低竞争环境下性能优异
  • 无死锁风险:基于硬件指令,不会产生死锁
  • 高并发:支持多个线程同时操作

二、原子更新基本类型类

2.1 AtomicBoolean - 原子更新布尔类型

使用场景:状态标志位、开关控制、条件判断

核心API详解

方法 参数 返回值 说明
get() - boolean 获取当前值
set(boolean newValue) newValue: 新值 void 设置新值
getAndSet(boolean newValue) newValue: 新值 boolean 原子性地设置为新值并返回旧值
compareAndSet(boolean expect, boolean update) expect: 期望值
update: 更新值
boolean 如果当前值等于期望值,则原子性地更新
lazySet(boolean newValue) newValue: 新值 void 最终设置为新值,但不保证立即可见性
weakCompareAndSet(boolean expect, boolean update) expect: 期望值
update: 更新值
boolean 可能更弱的CAS操作,在某些平台上性能更好
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * AtomicBoolean示例:用于原子性地更新布尔值
 * 典型场景:系统开关、状态标志等
 */
public class AtomicBooleanDemo {
    public static void main(String[] args) {
        // 创建AtomicBoolean,初始值为false
        AtomicBoolean atomicBoolean = new AtomicBoolean(false);
        
        // get(): 获取当前值
        System.out.println("初始值: " + atomicBoolean.get());
        
        // getAndSet(): 原子性地设置为true,返回旧值
        boolean oldValue = atomicBoolean.getAndSet(true);
        System.out.println("getAndSet旧值: " + oldValue + ", 新值: " + atomicBoolean.get());
        
        // compareAndSet(): 比较并设置
        boolean success = atomicBoolean.compareAndSet(true, false);
        System.out.println("CAS操作结果: " + success + ", 当前值: " + atomicBoolean.get());
        
        // lazySet(): 最终会设置,但不保证立即可见性
        atomicBoolean.lazySet(true);
        System.out.println("lazySet后的值: " + atomicBoolean.get());
        
        // weakCompareAndSet(): 弱版本CAS
        boolean weakSuccess = atomicBoolean.weakCompareAndSet(true, false);
        System.out.println("弱CAS操作结果: " + weakSuccess + ", 当前值: " + atomicBoolean.get());
    }
}

原理分析
AtomicBoolean内部实际上使用int类型来存储,0表示false,1表示true。通过compareAndSwapInt来实现原子操作。

2.2 AtomicInteger - 原子更新整型

使用场景:计数器、序列号生成、资源数量控制

核心API详解

方法 参数 返回值 说明
get() - int 获取当前值
set(int newValue) newValue: 新值 void 设置新值
getAndSet(int newValue) newValue: 新值 int 原子性地设置为新值并返回旧值
compareAndSet(int expect, int update) expect: 期望值
update: 更新值
boolean CAS操作
getAndIncrement() - int 原子递增,返回旧值
getAndDecrement() - int 原子递减,返回旧值
getAndAdd(int delta) delta: 增量 int 原子加法,返回旧值
incrementAndGet() - int 原子递增,返回新值
decrementAndGet() - int 原子递减,返回新值
addAndGet(int delta) delta: 增量 int 原子加法,返回新值
updateAndGet(IntUnaryOperator) operator: 更新函数 int 函数式更新
accumulateAndGet(int x, IntBinaryOperator) x: 参数
operator: 操作函数
int 累积计算
import java.util.concurrent.atomic.AtomicInteger;

/**
 * AtomicInteger是最常用的原子类之一
 * 适用于计数器、ID生成器等需要原子递增的场景
 */
public class AtomicIntegerDemo {
    public static void main(String[] args) {
        AtomicInteger atomicInt = new AtomicInteger(0);
        
        // 基础操作
        System.out.println("初始值: " + atomicInt.get());
        atomicInt.set(5);
        System.out.println("set(5)后: " + atomicInt.get());
        
        // 原子递增并返回旧值 - 常用于计数
        System.out.println("getAndIncrement: " + atomicInt.getAndIncrement()); // 返回5
        System.out.println("当前值: " + atomicInt.get()); // 6
        
        // 原子递减并返回旧值
        System.out.println("getAndDecrement: " + atomicInt.getAndDecrement()); // 返回6
        System.out.println("当前值: " + atomicInt.get()); // 5
        
        // 原子加法并返回旧值
        System.out.println("getAndAdd(10): " + atomicInt.getAndAdd(10)); // 返回5
        System.out.println("当前值: " + atomicInt.get()); // 15
        
        // 原子递增并返回新值
        System.out.println("incrementAndGet: " + atomicInt.incrementAndGet()); // 16
        
        // 原子加法并返回结果 - 适合批量增加
        int result = atomicInt.addAndGet(10);
        System.out.println("addAndGet(10)结果: " + result); // 26
        
        // 比较并设置 - 核心CAS操作
        boolean updated = atomicInt.compareAndSet(26, 30);
        System.out.println("CAS操作结果: " + updated + ", 当前值: " + atomicInt.get());
        
        // 获取并设置新值 - 适合重置操作
        int previous = atomicInt.getAndSet(40);
        System.out.println("getAndSet旧值: " + previous + ", 新值: " + atomicInt.get());
        
        // JDK8新增:函数式更新 - 更灵活的更新方式
        atomicInt.updateAndGet(x -> x * 2);
        System.out.println("updateAndGet(*2)后的值: " + atomicInt.get()); // 80
        
        // 累积计算
        atomicInt.accumulateAndGet(10, (x, y) -> x + y * 2);
        System.out.println("accumulateAndGet后的值: " + atomicInt.get()); // 100
    }
}

源码分析

public final int getAndIncrement() {
    // 自旋CAS:循环直到成功
    for (;;) {
        int current = get();          // 步骤1:获取当前值
        int next = current + 1;       // 步骤2:计算新值
        if (compareAndSet(current, next))  // 步骤3:CAS更新
            return current;           // 成功则返回旧值
    }
    // 如果CAS失败,说明有其他线程修改了值,循环重试
}

2.3 AtomicLong - 原子更新长整型

使用场景:大数值计数器、统计信息、唯一ID生成

核心API详解

方法 参数 返回值 说明
get() - long 获取当前值
set(long newValue) newValue: 新值 void 设置新值
getAndSet(long newValue) newValue: 新值 long 原子性地设置为新值并返回旧值
compareAndSet(long expect, long update) expect: 期望值
update: 更新值
boolean CAS操作
getAndIncrement() - long 原子递增,返回旧值
getAndDecrement() - long 原子递减,返回旧值
getAndAdd(long delta) delta: 增量 long 原子加法,返回旧值
incrementAndGet() - long 原子递增,返回新值
decrementAndGet() - long 原子递减,返回新值
addAndGet(long delta) delta: 增量 long 原子加法,返回新值
updateAndGet(LongUnaryOperator) operator: 更新函数 long 函数式更新
accumulateAndGet(long x, LongBinaryOperator) x: 参数
operator: 操作函数
long 累积计算
import java.util.concurrent.atomic.AtomicLong;

/**
 * AtomicLong用于长整型的原子操作
 * 在64位系统中性能与AtomicInteger相当
 */
public class AtomicLongDemo {
    public static void main(String[] args) {
        AtomicLong atomicLong = new AtomicLong(100L);
        
        System.out.println("初始值: " + atomicLong.get());
        
        // 原子递增并返回旧值 - 适合序列号生成
        System.out.println("getAndIncrement: " + atomicLong.getAndIncrement());
        System.out.println("当前值: " + atomicLong.get());
        
        // 原子递减并返回旧值
        System.out.println("getAndDecrement: " + atomicLong.getAndDecrement());
        System.out.println("当前值: " + atomicLong.get());
        
        // 原子加法并返回旧值
        System.out.println("getAndAdd(50): " + atomicLong.getAndAdd(50L));
        System.out.println("当前值: " + atomicLong.get());
        
        // 原子递增并返回新值
        System.out.println("incrementAndGet: " + atomicLong.incrementAndGet());
        
        // 原子加法并返回结果 - 适合统计累加
        long newValue = atomicLong.addAndGet(50L);
        System.out.println("addAndGet(50)结果: " + newValue);
        
        // 比较并设置
        boolean success = atomicLong.compareAndSet(250L, 300L);
        System.out.println("CAS操作结果: " + success + ", 当前值: " + atomicLong.get());
        
        // JDK8新增:函数式更新
        atomicLong.updateAndGet(x -> x / 2);
        System.out.println("updateAndGet(/2)后的值: " + atomicLong.get());
        
        // JDK8新增:累积计算 - 适合复杂的原子计算
        atomicLong.accumulateAndGet(100L, (x, y) -> x * y);
        System.out.println("accumulateAndGet后的值: " + atomicLong.get());
    }
}

性能提示
在32位系统上,AtomicLong的CAS操作可能需要锁住总线,性能相对较差。Java 8提供了LongAdder作为高性能替代方案。

三、原子更新数组类

3.1 AtomicIntegerArray - 原子更新整型数组

使用场景:并发计数器数组、桶统计、并行计算

核心API详解

方法 参数 返回值 说明
length() - int 返回数组长度
get(int i) i: 索引 int 获取指定索引的值
set(int i, int newValue) i: 索引
newValue: 新值
void 设置指定索引的值
getAndSet(int i, int newValue) i: 索引
newValue: 新值
int 原子设置并返回旧值
compareAndSet(int i, int expect, int update) i: 索引
expect: 期望值
update: 更新值
boolean 对指定索引进行CAS操作
getAndIncrement(int i) i: 索引 int 原子递增指定索引,返回旧值
getAndDecrement(int i) i: 索引 int 原子递减指定索引,返回旧值
getAndAdd(int i, int delta) i: 索引
delta: 增量
int 原子加法,返回旧值
incrementAndGet(int i) i: 索引 int 原子递增指定索引,返回新值
addAndGet(int i, int delta) i: 索引
delta: 增量
int 原子加法,返回新值
import java.util.concurrent.atomic.AtomicIntegerArray;

/**
 * AtomicIntegerArray允许原子地更新数组中的单个元素
 * 注意:构造函数会复制传入的数组,不影响原数组
 */
public class AtomicIntegerArrayDemo {
    public static void main(String[] args) {
        int[] initialArray = {1, 2, 3, 4, 5};
        // 创建原子整型数组,会复制传入的数组
        AtomicIntegerArray atomicArray = new AtomicIntegerArray(initialArray);
        
        System.out.println("数组长度: " + atomicArray.length());
        System.out.println("原始数组: " + atomicArray.toString());
        
        // get(): 获取指定索引的值
        System.out.println("索引0的值: " + atomicArray.get(0));
        
        // set(): 设置指定索引的值
        atomicArray.set(0, 10);
        System.out.println("set(0, 10)后的数组: " + atomicArray.toString());
        
        // getAndSet(): 原子更新指定索引的元素并返回旧值
        int oldValue = atomicArray.getAndSet(1, 20);
        System.out.println("索引1替换前的值: " + oldValue + ", 数组: " + atomicArray.toString());
        
        // getAndIncrement(): 原子递增指定索引的元素 - 适合分桶计数
        oldValue = atomicArray.getAndIncrement(2);
        System.out.println("索引2递增前值: " + oldValue + ", 数组: " + atomicArray.toString());
        
        // compareAndSet(): 比较并设置特定位置的元素
        boolean updated = atomicArray.compareAndSet(3, 4, 40);
        System.out.println("索引3 CAS结果: " + updated + ", 数组: " + atomicArray.toString());
        
        // addAndGet(): 原子加法 - 适合累加统计
        int newValue = atomicArray.addAndGet(4, 5);
        System.out.println("索引4加5后的值: " + newValue + ", 数组: " + atomicArray.toString());
        
        // incrementAndGet(): 原子递增并返回新值
        newValue = atomicArray.incrementAndGet(0);
        System.out.println("索引0递增后的值: " + newValue);
        
        // 重要:原始数组不会被修改
        System.out.println("原始数组值未被修改: " + initialArray[0]); // 仍然是1
    }
}

设计思想
AtomicIntegerArray通过复制数组来避免外部修改,每个数组元素的更新都是独立的原子操作。

3.2 AtomicLongArray - 原子更新长整型数组

使用场景:大数据统计、时间戳数组、大数值桶统计

核心API详解

方法 参数 返回值 说明
length() - int 返回数组长度
get(int i) i: 索引 long 获取指定索引的值
set(int i, long newValue) i: 索引
newValue: 新值
void 设置指定索引的值
getAndSet(int i, long newValue) i: 索引
newValue: 新值
long 原子设置并返回旧值
compareAndSet(int i, long expect, long update) i: 索引
expect: 期望值
update: 更新值
boolean 对指定索引进行CAS操作
getAndAdd(int i, long delta) i: 索引
delta: 增量
long 原子加法,返回旧值
addAndGet(int i, long delta) i: 索引
delta: 增量
long 原子加法,返回新值
import java.util.concurrent.atomic.AtomicLongArray;

/**
 * AtomicLongArray提供长整型数组的原子操作
 * 适用于需要大数值范围的并发统计
 */
public class AtomicLongArrayDemo {
    public static void main(String[] args) {
        long[] initialArray = {100L, 200L, 300L, 400L, 500L};
        AtomicLongArray atomicLongArray = new AtomicLongArray(initialArray);
        
        System.out.println("数组长度: " + atomicLongArray.length());
        System.out.println("初始数组: " + atomicLongArray.toString());
        
        // 基础操作
        System.out.println("索引0的值: " + atomicLongArray.get(0));
        atomicLongArray.set(0, 150L);
        System.out.println("set(0, 150)后的数组: " + atomicLongArray.toString());
        
        // 原子更新操作
        long oldValue = atomicLongArray.getAndSet(1, 250L);
        System.out.println("索引1替换前的值: " + oldValue + ", 数组: " + atomicLongArray.toString());
        
        // 原子加法操作
        atomicLongArray.getAndAdd(2, 100L);
        System.out.println("索引2加100后的数组: " + atomicLongArray.toString());
        
        // 比较并设置
        atomicLongArray.compareAndSet(3, 400L, 450L);
        System.out.println("索引3 CAS后的数组: " + atomicLongArray.toString());
        
        // 加法并获取新值
        long newValue = atomicLongArray.addAndGet(4, 200L);
        System.out.println("索引4加200后的值: " + newValue);
    }
}

3.3 AtomicReferenceArray - 原子更新引用类型数组

使用场景:对象池、缓存数组、并发数据结构

核心API详解

方法 参数 返回值 说明
length() - int 返回数组长度
get(int i) i: 索引 E 获取指定索引的引用
set(int i, E newValue) i: 索引
newValue: 新引用
void 设置指定索引的引用
getAndSet(int i, E newValue) i: 索引
newValue: 新引用
E 原子设置并返回旧引用
compareAndSet(int i, E expect, E update) i: 索引
expect: 期望引用
update: 更新引用
boolean 对指定索引进行CAS操作
lazySet(int i, E newValue) i: 索引
newValue: 新引用
void 延迟设置引用
import java.util.concurrent.atomic.AtomicReferenceArray;

/**
 * AtomicReferenceArray用于原子更新引用类型数组
 * 适用于对象引用需要原子更新的场景
 */
public class AtomicReferenceArrayDemo {
    static class Person {
        String name;
        int age;
        
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        
        @Override
        public String toString() {
            return name + "(" + age + ")";
        }
    }
    
    public static void main(String[] args) {
        Person[] persons = {
            new Person("Alice", 25),
            new Person("Bob", 30),
            new Person("Charlie", 35),
            new Person("David", 40)
        };
        
        AtomicReferenceArray<Person> atomicArray = new AtomicReferenceArray<>(persons);
        
        System.out.println("数组长度: " + atomicArray.length());
        System.out.println("初始数组: ");
        for (int i = 0; i < atomicArray.length(); i++) {
            System.out.println("索引 " + i + ": " + atomicArray.get(i));
        }
        
        // 原子更新引用 - 适合对象替换
        Person newPerson = new Person("Eve", 28);
        Person oldPerson = atomicArray.getAndSet(1, newPerson);
        System.out.println("索引1替换: " + oldPerson + " -> " + atomicArray.get(1));
        
        // 比较并设置引用
        boolean success = atomicArray.compareAndSet(2, persons[2], new Person("Frank", 45));
        System.out.println("索引2 CAS结果: " + success + ", 新值: " + atomicArray.get(2));
        
        // 延迟设置
        atomicArray.lazySet(3, new Person("Grace", 50));
        System.out.println("索引3延迟设置后的值: " + atomicArray.get(3));
        
        // 遍历数组
        System.out.println("最终数组状态:");
        for (int i = 0; i < atomicArray.length(); i++) {
            System.out.println("索引 " + i + ": " + atomicArray.get(i));
        }
    }
}

四、原子更新引用类型

4.1 AtomicReference - 原子更新引用类型

使用场景:单例模式、缓存更新、状态对象替换

核心API详解

方法 参数 返回值 说明
get() - V 获取当前引用
set(V newValue) newValue: 新引用 void 设置新引用
getAndSet(V newValue) newValue: 新引用 V 原子设置并返回旧引用
compareAndSet(V expect, V update) expect: 期望引用
update: 更新引用
boolean CAS操作
weakCompareAndSet(V expect, V update) expect: 期望引用
update: 更新引用
boolean 弱版本CAS
lazySet(V newValue) newValue: 新引用 void 延迟设置引用
updateAndGet(UnaryOperator<V>) operator: 更新函数 V 函数式更新
getAndUpdate(UnaryOperator<V>) operator: 更新函数 V 函数式更新并返回旧值
accumulateAndGet(V x, BinaryOperator<V>) x: 参数
operator: 操作函数
V 累积计算
import java.util.concurrent.atomic.AtomicReference;

/**
 * AtomicReference用于原子更新对象引用
 * 解决"先检查后执行"的竞态条件
 */
public class AtomicReferenceDemo {
    static class User {
        private String name;
        private int age;
        
        public User(String name, int age) {
            this.name = name;
            this.age = age;
        }
        
        public String getName() { return name; }
        public int getAge() { return age; }
        
        @Override
        public String toString() {
            return "User{name='" + name + "', age=" + age + "}";
        }
    }
    
    public static void main(String[] args) {
        AtomicReference<User> atomicUser = new AtomicReference<>();
        
        User initialUser = new User("张三", 25);
        atomicUser.set(initialUser);
        
        System.out.println("初始用户: " + atomicUser.get());
        
        // getAndSet(): 原子更新引用 - 适合缓存更新
        User newUser = new User("李四", 30);
        User oldUser = atomicUser.getAndSet(newUser);
        System.out.println("替换前的用户: " + oldUser);
        System.out.println("当前用户: " + atomicUser.get());
        
        // compareAndSet(): 比较并设置 - 核心操作
        boolean success = atomicUser.compareAndSet(newUser, new User("王五", 35));
        System.out.println("CAS操作结果: " + success + ", 当前用户: " + atomicUser.get());
        
        // weakCompareAndSet(): 弱版本CAS
        boolean weakSuccess = atomicUser.weakCompareAndSet(
            atomicUser.get(), new User("赵六", 40));
        System.out.println("弱CAS操作结果: " + weakSuccess + ", 当前用户: " + atomicUser.get());
        
        // lazySet(): 延迟设置
        atomicUser.lazySet(new User("孙七", 45));
        System.out.println("延迟设置后的用户: " + atomicUser.get());
        
        // JDK8新增:函数式更新
        atomicUser.updateAndGet(user -> new User(user.getName() + "_updated", user.getAge() + 1));
        System.out.println("函数式更新后的用户: " + atomicUser.get());
        
        // getAndUpdate(): 函数式更新并返回旧值
        User previous = atomicUser.getAndUpdate(user -> new User("周八", 50));
        System.out.println("更新前的用户: " + previous + ", 当前用户: " + atomicUser.get());
        
        // accumulateAndGet(): 累积计算
        atomicUser.accumulateAndGet(new User("吴九", 55), 
            (old, param) -> new User(old.getName() + "&" + param.getName(), 
                                   old.getAge() + param.getAge()));
        System.out.println("累积计算后的用户: " + atomicUser.get());
    }
}

典型应用:单例模式的双重检查锁定

class Singleton {
    private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<>();
    
    public static Singleton getInstance() {
        for (;;) {
            Singleton current = INSTANCE.get();
            if (current != null) return current;
            
            current = new Singleton();
            if (INSTANCE.compareAndSet(null, current)) {
                return current;
            }
        }
    }
}

4.2 AtomicMarkableReference - 带标记位的原子引用

使用场景:带状态的缓存、ABA问题简单解决方案

核心API详解

方法 参数 返回值 说明
getReference() - V 获取当前引用
isMarked() - boolean 获取当前标记位
get(boolean[] markHolder) markHolder: 标记位容器 V 获取引用和标记位
set(V newReference, boolean newMark) newReference: 新引用
newMark: 新标记
void 设置引用和标记位
compareAndSet(V expectedReference, V newReference, boolean expectedMark, boolean newMark) expectedReference: 期望引用
newReference: 新引用
expectedMark: 期望标记
newMark: 新标记
boolean 同时比较引用和标记位
attemptMark(V expectedReference, boolean newMark) expectedReference: 期望引用
newMark: 新标记
boolean 尝试只更新标记位
import java.util.concurrent.atomic.AtomicMarkableReference;

/**
 * AtomicMarkableReference将引用与一个布尔标记位绑定
 * 适用于需要同时更新引用和状态的场景
 */
public class AtomicMarkableReferenceDemo {
    public static void main(String[] args) {
        String initialRef = "初始数据";
        boolean initialMark = false;
        
        // 创建带标记位的原子引用
        AtomicMarkableReference<String> atomicMarkableRef = 
            new AtomicMarkableReference<>(initialRef, initialMark);
        
        System.out.println("初始引用: " + atomicMarkableRef.getReference());
        System.out.println("初始标记: " + atomicMarkableRef.isMarked());
        
        // get(boolean[]): 同时获取引用和标记位
        boolean[] markHolder = new boolean[1];
        String currentRef = atomicMarkableRef.get(markHolder);
        System.out.println("当前引用: " + currentRef + ", 当前标记: " + markHolder[0]);
        
        // compareAndSet(): 尝试同时更新引用和标记位
        String newRef = "新数据";
        boolean newMark = true;
        boolean success = atomicMarkableRef.compareAndSet(
            initialRef, newRef, initialMark, newMark);
        System.out.println("CAS操作结果: " + success);
        System.out.println("新引用: " + atomicMarkableRef.getReference());
        System.out.println("新标记: " + atomicMarkableRef.isMarked());
        
        // attemptMark(): 只尝试更新标记位
        boolean markUpdated = atomicMarkableRef.attemptMark(newRef, false);
        System.out.println("标记更新结果: " + markUpdated);
        System.out.println("最终标记: " + atomicMarkableRef.isMarked());
        
        // set(): 直接设置引用和标记位
        atomicMarkableRef.set("最终数据", true);
        System.out.println("直接设置后的引用: " + atomicMarkableRef.getReference());
        System.out.println("直接设置后的标记: " + atomicMarkableRef.isMarked());
    }
}

4.3 AtomicStampedReference - 带版本号的原子引用

使用场景:解决ABA问题、乐观锁实现

核心API详解

方法 参数 返回值 说明
getReference() - V 获取当前引用
getStamp() - int 获取当前版本号
get(int[] stampHolder) stampHolder: 版本号容器 V 获取引用和版本号
set(V newReference, int newStamp) newReference: 新引用
newStamp: 新版本号
void 设置引用和版本号
compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp) expectedReference: 期望引用
newReference: 新引用
expectedStamp: 期望版本号
newStamp: 新版本号
boolean 同时比较引用和版本号
attemptStamp(V expectedReference, int newStamp) expectedReference: 期望引用
newStamp: 新版本号
boolean 尝试只更新版本号
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * AtomicStampedReference通过版本号解决ABA问题
 * 每次修改都会增加版本号,确保不会误判
 */
public class AtomicStampedReferenceDemo {
    public static void main(String[] args) {
        String initialRef = "数据A";
        int initialStamp = 0;
        
        // 创建带版本号的原子引用
        AtomicStampedReference<String> atomicStampedRef = 
            new AtomicStampedReference<>(initialRef, initialStamp);
        
        System.out.println("初始引用: " + atomicStampedRef.getReference());
        System.out.println("初始版本号: " + atomicStampedRef.getStamp());
        
        // get(int[]): 同时获取引用和版本号
        int[] stampHolder = new int[1];
        String currentRef = atomicStampedRef.get(stampHolder);
        System.out.println("当前引用: " + currentRef + ", 当前版本号: " + stampHolder[0]);
        
        // 模拟ABA问题场景
        String newRefB = "数据B";
        String newRefA = "数据A"; // 又改回A,但版本号不同
        
        // 第一次更新:A -> B,版本号 0 -> 1
        boolean firstUpdate = atomicStampedRef.compareAndSet(
            initialRef, newRefB, initialStamp, initialStamp + 1);
        System.out.println("第一次更新(A->B)结果: " + firstUpdate);
        System.out.println("当前引用: " + atomicStampedRef.getReference());
        System.out.println("当前版本号: " + atomicStampedRef.getStamp());
        
        // 第二次更新:B -> A,版本号 1 -> 2
        boolean secondUpdate = atomicStampedRef.compareAndSet(
            newRefB, newRefA, 1, 2);
        System.out.println("第二次更新(B->A)结果: " + secondUpdate);
        System.out.println("当前引用: " + atomicStampedRef.getReference());
        System.out.println("当前版本号: " + atomicStampedRef.getStamp());
        
        // 尝试用旧版本号更新(会失败)- 这就是解决ABA问题的关键!
        boolean failedUpdate = atomicStampedRef.compareAndSet(
            newRefA, "新数据", 0, 1); // 使用旧的版本号0
        System.out.println("使用旧版本号更新结果: " + failedUpdate);
        System.out.println("引用未被修改: " + atomicStampedRef.getReference());
        
        // attemptStamp(): 只更新版本号
        boolean stampUpdated = atomicStampedRef.attemptStamp(newRefA, 3);
        System.out.println("版本号更新结果: " + stampUpdated);
        System.out.println("新版本号: " + atomicStampedRef.getStamp());
        
        // 正确的方式:使用当前版本号
        stampHolder = new int[1];
        currentRef = atomicStampedRef.get(stampHolder);
        boolean correctUpdate = atomicStampedRef.compareAndSet(
            currentRef, "最终数据", stampHolder[0], stampHolder[0] + 1);
        System.out.println("使用正确版本号更新结果: " + correctUpdate);
        System.out.println("最终引用: " + atomicStampedRef.getReference());
        System.out.println("最终版本号: " + atomicStampedRef.getStamp());
    }
}

ABA问题详解
ABA问题是指:

  1. 线程1读取值A
  2. 线程2将值改为B,然后又改回A
  3. 线程1进行CAS操作,发现当前值仍是A,于是操作成功

虽然值看起来没变,但中间状态的变化可能对业务逻辑产生影响。AtomicStampedReference通过版本号完美解决了这个问题。

五、原子更新字段类

5.1 AtomicIntegerFieldUpdater - 原子更新整型字段

使用场景:优化内存使用、大量对象需要原子字段更新

核心API详解

方法 参数 返回值 说明
newUpdater(Class<U> tclass, String fieldName) tclass: 目标类
fieldName: 字段名
AtomicIntegerFieldUpdater<U> 静态方法创建更新器
get(U obj) obj: 目标对象 int 获取字段值
set(U obj, int newValue) obj: 目标对象
newValue: 新值
void 设置字段值
getAndSet(U obj, int newValue) obj: 目标对象
newValue: 新值
int 原子设置并返回旧值
compareAndSet(U obj, int expect, int update) obj: 目标对象
expect: 期望值
update: 更新值
boolean CAS操作
getAndIncrement(U obj) obj: 目标对象 int 原子递增,返回旧值
getAndDecrement(U obj) obj: 目标对象 int 原子递减,返回旧值
getAndAdd(U obj, int delta) obj: 目标对象
delta: 增量
int 原子加法,返回旧值
incrementAndGet(U obj) obj: 目标对象 int 原子递增,返回新值
addAndGet(U obj, int delta) obj: 目标对象
delta: 增量
int 原子加法,返回新值
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

/**
 * AtomicIntegerFieldUpdater以原子方式更新对象的volatile int字段
 * 相比为每个对象创建AtomicInteger,可以节省大量内存
 */
public class AtomicIntegerFieldUpdaterDemo {
    static class Counter {
        // 必须用volatile修饰,保证可见性
        public volatile int count;
        private String name;
        
        public Counter(String name, int initialCount) {
            this.name = name;
            this.count = initialCount;
        }
        
        public String getName() { return name; }
        public int getCount() { return count; }
    }
    
    public static void main(String[] args) {
        // 创建字段更新器,指定要更新的类和字段名
        AtomicIntegerFieldUpdater<Counter> updater = 
            AtomicIntegerFieldUpdater.newUpdater(Counter.class, "count");
        
        Counter counter1 = new Counter("计数器1", 0);
        Counter counter2 = new Counter("计数器2", 10);
        
        System.out.println("计数器1初始计数: " + counter1.getCount());
        System.out.println("计数器2初始计数: " + counter2.getCount());
        
        // get(): 获取字段值
        System.out.println("通过updater获取计数器1的值: " + updater.get(counter1));
        
        // set(): 设置字段值
        updater.set(counter1, 5);
        System.out.println("设置计数器1为5后的值: " + counter1.getCount());
        
        // getAndIncrement(): 原子递增 - 相比synchronized性能更好
        int oldCount = updater.getAndIncrement(counter1);
        System.out.println("计数器1递增前值: " + oldCount + ", 当前值: " + counter1.getCount());
        
        // getAndAdd(): 原子加法
        oldCount = updater.getAndAdd(counter1, 10);
        System.out.println("计数器1加10前值: " + oldCount + ", 当前值: " + counter1.getCount());
        
        // incrementAndGet(): 原子递增并返回新值
        int newCount = updater.incrementAndGet(counter1);
        System.out.println("计数器1递增后的值: " + newCount);
        
        // addAndGet(): 原子加法并返回新值
        newCount = updater.addAndGet(counter1, 20);
        System.out.println("计数器1加20后的值: " + newCount);
        
        // compareAndSet(): 比较并设置
        boolean updated = updater.compareAndSet(counter1, 36, 50);
        System.out.println("计数器1 CAS操作结果: " + updated + ", 当前值: " + counter1.getCount());
        
        // 可以同时更新多个对象的相同字段
        updater.incrementAndGet(counter2);
        System.out.println("计数器2递增后的值: " + counter2.getCount());
    }
}

内存优化效果

  • AtomicInteger对象:16-24字节 overhead
  • volatile int + AtomicIntegerFieldUpdater:4字节 + 静态updater
  • 当有大量对象时,内存节省效果显著

5.2 AtomicLongFieldUpdater - 原子更新长整型字段

使用场景:大数值字段的原子更新、内存敏感场景

核心API详解

方法 参数 返回值 说明
newUpdater(Class<U> tclass, String fieldName) tclass: 目标类
fieldName: 字段名
AtomicLongFieldUpdater<U> 静态方法创建更新器
get(U obj) obj: 目标对象 long 获取字段值
set(U obj, long newValue) obj: 目标对象
newValue: 新值
void 设置字段值
getAndSet(U obj, long newValue) obj: 目标对象
newValue: 新值
long 原子设置并返回旧值
compareAndSet(U obj, long expect, long update) obj: 目标对象
expect: 期望值
update: 更新值
boolean CAS操作
getAndAdd(U obj, long delta) obj: 目标对象
delta: 增量
long 原子加法,返回旧值
addAndGet(U obj, long delta) obj: 目标对象
delta: 增量
long 原子加法,返回新值
import java.util.concurrent.atomic.AtomicLongFieldUpdater;

/**
 * AtomicLongFieldUpdater用于原子更新long字段
 * 适用于需要大数值范围且内存敏感的场景
 */
public class AtomicLongFieldUpdaterDemo {
    static class Account {
        // 必须用volatile修饰
        public volatile long balance;
        private final String owner;
        
        public Account(String owner, long initialBalance) {
            this.owner = owner;
            this.balance = initialBalance;
        }
        
        public String getOwner() { return owner; }
        public long getBalance() { return balance; }
    }
    
    public static void main(String[] args) {
        AtomicLongFieldUpdater<Account> balanceUpdater = 
            AtomicLongFieldUpdater.newUpdater(Account.class, "balance");
        
        Account account1 = new Account("张三", 1000L);
        Account account2 = new Account("李四", 2000L);
        
        System.out.println("张三账户初始余额: " + account1.getBalance());
        System.out.println("李四账户初始余额: " + account2.getBalance());
        
        // 基础操作
        System.out.println("通过updater获取张三余额: " + balanceUpdater.get(account1));
        balanceUpdater.set(account1, 1500L);
        System.out.println("设置张三余额为1500后的值: " + account1.getBalance());
        
        // 原子存款 - 无锁线程安全
        balanceUpdater.addAndGet(account1, 500L);
        System.out.println("张三存款500后余额: " + account1.getBalance());
        
        // 原子取款
        long oldBalance = balanceUpdater.getAndAdd(account1, -200L);
        System.out.println("张三取款200前余额: " + oldBalance + ", 取款后余额: " + account1.getBalance());
        
        // 比较并设置 - 实现转账等业务
        boolean transferSuccess = balanceUpdater.compareAndSet(account1, 1800L, 2000L);
        System.out.println("张三转账操作结果: " + transferSuccess + ", 当前余额: " + account1.getBalance());
        
        // 同时操作多个账户
        balanceUpdater.getAndAdd(account2, 1000L);
        System.out.println("李四存款1000后余额: " + account2.getBalance());
    }
}

5.3 AtomicReferenceFieldUpdater - 原子更新引用字段

使用场景:链表节点更新、树结构调整、对象关系维护

核心API详解

方法 参数 返回值 说明
newUpdater(Class<U> tclass, Class<W> vclass, String fieldName) tclass: 目标类
vclass: 字段类型
fieldName: 字段名
AtomicReferenceFieldUpdater<U,W> 静态方法创建更新器
get(U obj) obj: 目标对象 V 获取字段引用
set(U obj, V newValue) obj: 目标对象
newValue: 新引用
void 设置字段引用
getAndSet(U obj, V newValue) obj: 目标对象
newValue: 新引用
V 原子设置并返回旧引用
compareAndSet(U obj, V expect, V update) obj: 目标对象
expect: 期望引用
update: 更新引用
boolean CAS操作
lazySet(U obj, V newValue) obj: 目标对象
newValue: 新引用
void 延迟设置引用
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

/**
 * AtomicReferenceFieldUpdater用于原子更新引用字段
 * 常用于实现无锁数据结构
 */
public class AtomicReferenceFieldUpdaterDemo {
    static class Node<T> {
        // 必须用volatile修饰
        public volatile Node<T> next;
        private final T value;
        
        public Node(T value) {
            this.value = value;
        }
        
        public T getValue() { return value; }
        public Node<T> getNext() { return next; }
        
        @Override
        public String toString() {
            return "Node{value=" + value + ", next=" + (next != null ? next.value : "null") + "}";
        }
    }
    
    public static void main(String[] args) {
        // 创建引用字段更新器
        AtomicReferenceFieldUpdater<Node, Node> nextUpdater = 
            AtomicReferenceFieldUpdater.newUpdater(Node.class, Node.class, "next");
        
        Node<String> first = new Node<>("第一个节点");
        Node<String> second = new Node<>("第二个节点");
        Node<String> third = new Node<>("第三个节点");
        
        System.out.println("初始第一个节点的next: " + first.getNext());
        
        // get(): 获取字段引用
        System.out.println("通过updater获取第一个节点的next: " + nextUpdater.get(first));
        
        // set(): 设置字段引用
        nextUpdater.set(first, second);
        System.out.println("设置第一个节点的next为第二个节点: " + first);
        
        // compareAndSet(): 原子设置next字段 - 实现无锁链表
        boolean setSuccess = nextUpdater.compareAndSet(first, second, third);
        System.out.println("CAS操作结果: " + setSuccess);
        System.out.println("第一个节点: " + first);
        
        // getAndSet(): 获取并设置引用
        Node<String> oldNext = nextUpdater.getAndSet(second, third);
        System.out.println("第二个节点原来的next: " + oldNext);
        System.out.println("第二个节点: " + second);
        
        // lazySet(): 延迟设置
        nextUpdater.lazySet(third, first); // 形成环状,仅作演示
        System.out.println("第三个节点延迟设置后的next: " + third.getNext());
        
        // 构建链表并展示
        System.out.println("最终链表结构:");
        Node<String> current = first;
        int count = 0;
        while (current != null && count < 5) { // 防止无限循环
            System.out.println(current);
            current = current.getNext();
            count++;
        }
    }
}

六、综合实战:构建线程安全计数器

下面我们通过一个综合示例展示如何在实际项目中使用原子操作类:

import java.util.concurrent.atomic.*;
import java.util.concurrent.*;

/**
 * 线程安全计数器综合示例
 * 展示了多种原子类的实际应用
 */
public class ThreadSafeCounter {
    // 基本计数器 - 使用AtomicInteger
    private final AtomicInteger count = new AtomicInteger(0);
    
    // 大数值统计 - 使用AtomicLong  
    private final AtomicLong total = new AtomicLong(0L);
    
    // 状态控制 - 使用AtomicReference
    private final AtomicReference<String> status = new AtomicReference<>("RUNNING");
    
    // 统计数组 - 使用AtomicIntegerArray进行分桶统计
    private final AtomicIntegerArray bucketStats = new AtomicIntegerArray(10);
    
    // 配置信息 - 使用AtomicReference支持动态更新
    private final AtomicReference<Config> config = new AtomicReference<>(new Config(100, 60));
    
    // 标记位控制 - 使用AtomicBoolean
    private final AtomicBoolean enabled = new AtomicBoolean(true);
    
    static class Config {
        final int maxConnections;
        final int timeoutSeconds;
        
        public Config(int maxConnections, int timeoutSeconds) {
            this.maxConnections = maxConnections;
            this.timeoutSeconds = timeoutSeconds;
        }
        
        @Override
        public String toString() {
            return "Config{maxConnections=" + maxConnections + 
                   ", timeoutSeconds=" + timeoutSeconds + "}";
        }
    }
    
    // 核心API方法
    
    public void increment() {
        if (!enabled.get()) {
            System.out.println("计数器已禁用,忽略操作");
            return;
        }
        
        count.incrementAndGet();
        total.addAndGet(1L);
        
        // 分桶统计:根据count值决定放入哪个桶
        int bucket = count.get() % 10;
        bucketStats.getAndIncrement(bucket);
    }
    
    public void add(int value) {
        if (!enabled.get()) {
            System.out.println("计数器已禁用,忽略操作");
            return;
        }
        
        count.addAndGet(value);
        total.addAndGet(value);
    }
    
    public boolean setStatus(String expected, String newStatus) {
        return status.compareAndSet(expected, newStatus);
    }
    
    public void updateConfig(Config newConfig) {
        Config oldConfig;
        do {
            oldConfig = config.get();
            System.out.println("尝试更新配置: " + oldConfig + " -> " + newConfig);
        } while (!config.compareAndSet(oldConfig, newConfig));
        System.out.println("配置更新成功");
    }
    
    public boolean enable() {
        return enabled.compareAndSet(false, true);
    }
    
    public boolean disable() {
        return enabled.compareAndSet(true, false);
    }
    
    // 获取统计信息
    public void printStats() {
        System.out.println("\n=== 统计信息 ===");
        System.out.println("当前计数: " + count.get());
        System.out.println("总数: " + total.get());
        System.out.println("状态: " + status.get());
        System.out.println("启用状态: " + enabled.get());
        System.out.println("桶统计: " + bucketStats.toString());
        
        Config currentConfig = config.get();
        System.out.println("配置: " + currentConfig);
        
        // 验证数据一致性
        long sum = 0;
        for (int i = 0; i < bucketStats.length(); i++) {
            sum += bucketStats.get(i);
        }
        System.out.println("桶统计总和: " + sum + ", 计数: " + count.get() + 
                         ", 一致性: " + (sum == count.get()));
    }
    
    public static void main(String[] args) throws InterruptedException {
        ThreadSafeCounter counter = new ThreadSafeCounter();
        
        // 创建多个线程同时操作计数器
        int threadCount = 10;
        int operationsPerThread = 1000;
        
        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
        CountDownLatch latch = new CountDownLatch(threadCount);
        
        System.out.println("开始并发测试...");
        
        for (int i = 0; i < threadCount; i++) {
            final int threadId = i;
            executor.execute(() -> {
                try {
                    for (int j = 0; j < operationsPerThread; j++) {
                        counter.increment();
                        
                        // 每隔一定操作数更新配置
                        if (j % 200 == 0) {
                            counter.updateConfig(new Config(100 + j, 60));
                        }
                        
                        // 模拟随机禁用/启用
                        if (j == 500 && threadId == 0) {
                            System.out.println("线程" + threadId + "尝试禁用计数器");
                            counter.disable();
                            Thread.sleep(10); // 短暂休眠
                            System.out.println("线程" + threadId + "尝试启用计数器");
                            counter.enable();
                        }
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                } finally {
                    latch.countDown();
                }
            });
        }
        
        // 等待所有线程完成
        latch.await();
        executor.shutdown();
        
        // 打印最终统计
        counter.printStats();
        int expectedCount = threadCount * operationsPerThread;
        System.out.println("\n=== 测试结果 ===");
        System.out.println("期望计数: " + expectedCount);
        System.out.println("实际计数: " + counter.count.get());
        System.out.println("计数正确: " + (counter.count.get() == expectedCount));
        System.out.println("测试" + (counter.count.get() == expectedCount ? "通过" : "失败"));
    }
}

七、原子操作类的工作原理

7.1 CAS机制详解

CAS(Compare-And-Swap)是原子操作类的核心,包含三个操作数:

  • 内存位置(V)
  • 期望原值(A)
  • 新值(B)

CAS的语义是:"我认为V的值应该是A,如果是,那么将V的值更新为B,否则不修改并告诉我现在的值是多少"

CAS操作是硬件级别的原子操作,在现代CPU中通常通过以下方式实现:

  • x86架构CMPXCHG指令
  • ARM架构LDREX/STREX指令对

7.2 Unsafe类的作用

所有原子操作类底层都依赖sun.misc.Unsafe类,它提供了硬件级别的原子操作:

public final class Unsafe {
    // 对象字段操作
    public native long objectFieldOffset(Field f);
    
    // 数组基础偏移
    public native int arrayBaseOffset(Class arrayClass);
    
    // 数组索引缩放
    public native int arrayIndexScale(Class arrayClass);
    
    // CAS操作
    public final native boolean compareAndSwapObject(Object o, long offset, 
                                                   Object expected, Object x);
    public final native boolean compareAndSwapInt(Object o, long offset, 
                                                int expected, int x);
    public final native boolean compareAndSwapLong(Object o, long offset, 
                                                 long expected, long x);
    
    // 获取和设置值
    public native int getIntVolatile(Object o, long offset);
    public native void putIntVolatile(Object o, long offset, int x);
    
    // 延迟设置(有更弱的可见性保证)
    public native void putOrderedInt(Object o, long offset, int x);
}

7.3 内存屏障与可见性

原子操作类通过内存屏障保证可见性:

  • 写操作:在写入后插入写屏障,保证写入对其他线程可见
  • 读操作:在读取前插入读屏障,保证读取到最新值

Java内存模型中的屏障类型:

  • LoadLoad屏障:保证该屏障前的读操作先于屏障后的读操作完成
  • StoreStore屏障:保证该屏障前的写操作先于屏障后的写操作完成
  • LoadStore屏障:保证该屏障前的读操作先于屏障后的写操作完成
  • StoreLoad屏障:保证该屏障前的所有写操作对其他处理器可见

八、性能对比与选型建议

8.1 性能对比

场景 synchronized 原子操作类 性能提升
低竞争 2-10倍
中等竞争 中等 中等 相当
高竞争 慢(自旋) 可能更差

8.2 不同原子类的性能特点

原子类 适用场景 性能特点
AtomicInteger 普通计数器 性能优秀,适用大部分场景
AtomicLong 大数值计数 在32位系统上性能较差
LongAdder 高并发统计 高竞争环境下性能最优
AtomicReference 对象引用更新 性能与对象大小相关
字段更新器 内存敏感场景 节省内存,性能稍差

8.3 选型指南

  1. 计数器场景

    • 简单计数:AtomicInteger
    • 大数值计数:AtomicLongLongAdder
    • 分桶统计:AtomicIntegerArray
  2. 状态控制

    • 布尔标志:AtomicBoolean
    • 对象状态:AtomicReference
    • 带版本状态:AtomicStampedReference
  3. 内存敏感场景

    • 大量对象:字段更新器(AtomicXXXFieldUpdater
    • 缓存系统:AtomicReference
  4. 数据结构

    • 无锁队列:AtomicReference
    • 无锁栈:AtomicReference
    • 无锁链表:AtomicReferenceFieldUpdater

8.4 最佳实践

  1. 避免过度使用:不是所有场景都需要原子类
  2. 注意ABA问题:必要时使用带版本号的原子类
  3. 考虑高竞争:高竞争环境下考虑LongAdder等替代方案
  4. 内存布局:字段更新器可以优化内存使用
  5. JDK8+特性:利用新的函数式更新方法
  6. 性能测试:在实际环境中进行性能测试

九、总结

Java原子操作类为我们提供了强大的无锁并发编程工具:

9.1 核心价值

  • 13个原子类覆盖了基本类型、数组、引用和字段更新
  • CAS机制基于硬件指令,性能优异
  • 无锁设计避免了死锁和锁开销
  • 丰富的API支持各种并发场景

9.2 使用场景总结

类别 主要类 核心用途
基本类型 AtomicInteger, AtomicLong, AtomicBoolean 计数器、状态标志
数组 AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray 并发数组、分桶统计
引用 AtomicReference, AtomicStampedReference, AtomicMarkableReference 对象缓存、状态管理
字段更新 AtomicIntegerFieldUpdater, AtomicLongFieldUpdater, AtomicReferenceFieldUpdater 内存优化、大量对象

9.3 学习建议

  1. 从简单开始:先掌握AtomicIntegerAtomicReference
  2. 理解原理:深入理解CAS机制和内存模型
  3. 实践应用:在真实项目中尝试使用原子类
  4. 性能调优:根据实际场景选择合适的原子类
  5. 持续学习:关注JDK新版本中的并发工具改进

掌握这些原子操作类,能够让我们在适当的场景下写出更高效、更安全的并发代码。记住,工具虽好,但要因地制宜,根据具体场景选择最合适的并发控制方案。

希望本文能帮助你深入理解Java原子操作类,在实际项目中游刃有余地处理并发问题!

进一步学习资源

posted @ 2025-10-30 08:41  佛祖让我来巡山  阅读(123)  评论(0)    收藏  举报

佛祖让我来巡山博客站 - 创建于 2018-08-15

开发工程师个人站,内容主要是网站开发方面的技术文章,大部分来自学习或工作,部分来源于网络,希望对大家有所帮助。

Bootstrap中文网