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

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

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

1.1 问题的由来

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

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

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

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

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

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

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

java 复制代码
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操作,在某些平台上性能更好
java 复制代码
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 累积计算
java 复制代码
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
    }
}

源码分析:

java 复制代码
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 累积计算
java 复制代码
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 原子加法,返回新值
java 复制代码
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 原子加法,返回新值
java 复制代码
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 延迟设置引用
java 复制代码
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 累积计算
java 复制代码
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());
    }
}

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

java 复制代码
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 尝试只更新标记位
java 复制代码
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 尝试只更新版本号
java 复制代码
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 原子加法,返回新值
java 复制代码
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 原子加法,返回新值
java 复制代码
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 延迟设置引用
java 复制代码
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++;
        }
    }
}

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

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

java 复制代码
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类,它提供了硬件级别的原子操作:

java 复制代码
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
    • 大数值计数:AtomicLong 或 LongAdder
    • 分桶统计: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. 从简单开始 :先掌握AtomicInteger和AtomicReference
  2. 理解原理:深入理解CAS机制和内存模型
  3. 实践应用:在真实项目中尝试使用原子类
  4. 性能调优:根据实际场景选择合适的原子类
  5. 持续学习:关注JDK新版本中的并发工具改进

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

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

进一步学习资源:

相关推荐
吴声子夜歌4 个月前
JVM——原子操作类实现原理
jvm·原子操作类