java.util.concurrent.atomic
包,提供了很多原子操作类,这些类基本上都是使用 Unsafe 实现的包装类。
注意是包装类,类属性有: value
, value
的地址偏移量等。
# 原子更新基本类型
atomic
包提供 boolean
, int
, long
的原子更新操作。以 AtomicInteger
为例讲解,常用方法为:
addAndGet(int delta)
:原子相加compareAndSet(int except, int update)
:cas
操作getAndIncrement()
:原子自增lazySet(int newValue)
:最终会设置为新值,但是其他线程可能在之后一小段时间还是会看到旧值
需要注意的是, value
必须被 volatile
修饰,保证变量被修改时对所有线程可见。
private volatile int value; |
在 Unsafe
类中,只提供了 Object
, int
, long
的 cas
操作,所以想要实现 boolean
, float
等其他类型的原子包装类,就要想着将其转为 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 章