JAVA~原子操作类
Atomic原子操作类主要利用CAS (compare and swap) + volatile + native 方法来保证原子操作,从而避免 synchronized 的高开销。
- CAS:期望的值和原来的旧值比较,如果相同则操作,不同则什么也不做
- native unsafe方法:UnSafe 类的 objectFieldOffset() 方法是一个本地方法,用于获取”原来的值“的内存地址。提供三种cas方法。
- volatile value:修饰value,保证多线程环境下的可见性,即任何线程任何时刻都可以获取最新值。
0、Unsafe类
-
Unsafe只提供了3种CAS方法:compareAndSwapObject、compareAndSwapInt和compareAndSwapLong。都是native方法。
-
内部使用自旋的方式进行CAS更新(while循环进行CAS更新,如果更新失败,则循环再次重试)
-
compareAndSwapXX方法参数描述
参数名 描述 paramObject 需要操作的对象 paramLong1 内存地址 paramLong2 期望值 paramLong3 更新后的新值
//CAS内部使用自旋的方式进行CAS更新(while循环进行CAS更新,如果更新失败,则循环再次重试)
public final int getAndAddInt(Object paramObject, long paramLong, int paramInt){
int i;
do{
i = getIntVolatile(paramObject, paramLong);
}while (!compareAndSwapInt(paramObject, paramLong, i, i + paramInt));
return i;
}
public final long getAndAddLong(Object paramObject, long paramLong1, long paramLong2){
long l;
do{
l = getLongVolatile(paramObject, paramLong1);
}while (!compareAndSwapLong(paramObject, paramLong1, l, l + paramLong2));
return l;
}
public final int getAndSetInt(Object paramObject, long paramLong, int paramInt){
int i;
do{
i = getIntVolatile(paramObject, paramLong);
}while (!compareAndSwapInt(paramObject, paramLong, i, paramInt));
return i;
}
public final long getAndSetLong(Object paramObject, long paramLong1, long paramLong2){
long l;
do{
l = getLongVolatile(paramObject, paramLong1);
}while (!compareAndSwapLong(paramObject, paramLong1, l, paramLong2));
return l;
}
public final Object getAndSetObject(Object paramObject1, long paramLong, Object paramObject2){
Object localObject;
do{
localObject = getObjectVolatile(paramObject1, paramLong);
}while (!compareAndSwapObject(paramObject1, paramLong, localObject, paramObject2));
return localObject;
}
1、基本类型
AtomicInteger、AtomicLong、AtomicBoolean
- 所有原子操作类具有相同的结构(以 AtomicInteger为例)
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
//获取 ”原来值“的内存地址
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
//用volatile修饰,保证多线程下的数据可见性
private volatile int value;
- 原子操作类具有相似的方法名
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) //如果输入的数值等于预期值,则以原子方式将该值设置为输入值(update)
public final void lazySet(int newValue)//最终设置为newValue,使用 lazySet 设置之后可能导致其他线程在之后的一小段时间内还是可以读到旧的值
2、数组类型
AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
- 具有相似的方法名(以 AtomicIntegerArray为例)
public final int get(int i) //获取 index=i 位置元素的值
public final int getAndSet(int i, int newValue)//返回 index=i 位置的当前的值,并将其设置为新值:newValue
public final int getAndIncrement(int i)//获取 index=i 位置元素的值,并让该位置的元素自增
public final int getAndDecrement(int i) //获取 index=i 位置元素的值,并让该位置的元素自减
public final int getAndAdd(int delta) //获取 index=i 位置元素的值,并加上预期的值
public final boolean compareAndSet(int i, int expect, int update)//如果输入的数值等于预期值,则以原子方式将 index=i 位置的元素值设置为输入值(update)
public final void lazySet(int i, int newValue)//最终 将index=i 位置的元素设置为newValue,使用 lazySet 设置之后可能导致其他线程在之后的一小段时间内还是可以读到旧的值。
3、引用类型
AtomicReference、AtomicStampedReference、AtomicMarkableReference
- AtomicReference 的操作和AtomicInteger类似,只是把存入的数据类型变成了引用且调用unsafe的CAS方法不太一样。
- CAS有一个缺点就是ABA问题:即线程1和线程2都要操作内存中的同一个变量V,当线程1开始操作时,读取到内存的值为A,这时线程2也要操作同一变量V,然后将V的值修改成了B,但线程1还没有处理完,然后线程2又把变量V的值改回A,当线程1处理完后,要更新V的值时,发现旧值和内存中的值相同,从而完成更新。但其实V的值已经变过了,线程1不知道而已。
- 解决ABA问题办法:对每次操作加上版本,除了比较值是否被修改,还需要比较版本是否相同,以此来解决ABA问题。
- 原子操作类中
AtomicStampedReference和AtomicMarkableReference
实现了CAS带版本的功能,解决ABA问题。
//记录需要操作变量的信息:引用和版本(stamp)
private static class Pair<T> {
final T reference;
final int stamp;
private Pair(T reference, int stamp) {
this.reference = reference;
this.stamp = stamp;
}
static <T> Pair<T> of(T reference, int stamp) {
return new Pair<T>(reference, stamp);
}
}
//CAS更新操作时,需要保证 期望值的 引用和版本 与 ”当前值“(旧值)比较,相同时才能进行CAS,否则会失败
public boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp) {
Pair<V> current = pair;
return
expectedReference == current.reference &&
expectedStamp == current.stamp &&
((newReference == current.reference &&
newStamp == current.stamp) ||
casPair(current, Pair.of(newReference, newStamp)));
}