java.util.concurrent.atomic 包,提供了很多原子操作类,这些类基本上都是使用 Unsafe 实现的包装类

注意是包装类,类属性有: valuevalue 的地址偏移量等。

# 原子更新基本类型

atomic 包提供 booleanintlong 的原子更新操作。以 AtomicInteger 为例讲解,常用方法为:

  • addAndGet(int delta) :原子相加
  • compareAndSet(int except, int update)cas 操作
  • getAndIncrement() :原子自增
  • lazySet(int newValue) :最终会设置为新值,但是其他线程可能在之后一小段时间还是会看到旧值

需要注意的是, value 必须被 volatile 修饰,保证变量被修改时对所有线程可见

private volatile int value;

Unsafe 类中,只提供了 Objectintlongcas 操作,所以想要实现 booleanfloat 等其他类型的原子包装类,就要想着将其转为 int 再来实现。比如布尔可以映射为 0 和 1。

# 原子更新数组

原子更新数组里的某个元素, atomic 提供了三个类:

  • AtomicIntegerArray :原子更新整型数组里的某个元素
  • AtomicLongArray :原子更新长整型数组里的某个元素
  • AtomicReferenceArray :原子更新引用类型数组里的元素
static int[] value = {1,2};
    static AtomicIntegerArray ai = new AtomicIntegerAtomic(value);

数组通过构造方法传进去时会复制一份,所以包装类对元素的修改不会影响本来的数组。

public AtomicIntegerArray(int[] array) {
        // Visibility guaranteed by final field guarantees
        this.array = array.clone();
    }

# 原子更新引用类型

有时需要原子更新的是一个类里面多个属性,此时就需要使用原子更新引用:

  • AtomicReference :原子更新引用
  • AtomicReferenceFieldUpdater :原子更新引用类型里面的字段
  • AtomicMarkableReference :原子更新带有标记位的引用类型。
atomic.compareAndSet(oldUser,newUser);

其实使用的就是 Unsafe 类的原子更新 object 的方法:

public final boolean compareAndSet(V expect, V update) {
        return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
    }

# 原子更新字段类

原子更新字段类都是抽象类,需要使用静态方法 newUpdater() 创建一个更新其,并设置想要更新的类和属性。同时,这个属性必须被 volatile 修饰

atomic 包提供了三个类:

  • AtomicIntegerFieldUpdater :原子更新整型字段的更新器
  • AtomicLongFieldUpdater :原子更新长整型字段的更新器
  • AtomicStampedFieldUpdater :原子更新带有版本号的引用类型。该类将整数值和引用关联起来,原子更新数据以及数据的版本号,解决 ABA 问题。
public class Test {
    private static AtomicIntegerFieldUpdater<User> updater = 
        AtomicIntegerFieldUpdater.newUpdater(User.class, "age");
    
    public static void main(String[] args) {
        User user = new User("cyan", 11);
        a.getAndIncrement(conan)
        System.out.println(a.get(conan));
    }
}

# 参考

《Java 并发编程的艺术》第 7 章