java.util.concurrent.atomic源码学习(
1.常用类概览
先看一下atomic主要包含哪些常用类

2.原子类&原子数组
在介绍原子类&原子数组前,首先要引入两个重要的unsafe方法,分别是
/**
获得某个属性域在这个类的内存偏移,入参是一个Filed
由于底层方法直接操作内存,因此对于属性域在内存的位置是必要信息,在后续getAndSet操作以及CAS操作时都会用到
*/
unsafe.objectFieldOffset(Field var1);
/*这几个都是CAS的核心用法,入参分别是
1.待修改对象
2.属性域在这个对象的内存偏移量
3.excpect值
4.update值
CAS对比的时候就是比较寄存器的值是否等于excpect的值,是的话将其更新为update值
*/
unsafe.compareAndSwapLong(Object var1, long var2, long var4, long var6);
unsafe.compareAndSwapInt(Object var1, long var2, int var4, int var5);
unsafe.compareAndSwapObject(Object var1, long var2, Object var4, Object var5));
2.1 原子类
知道了这些底层的基本方法,这些类的实现也就显而易见了,常见的套路是在对应的AtomicInteger,AtomicLong,AtomicReference开辟一个私有的value
域,存放这个初始值,同时持有一个final long类型的valueOffset
存在这个value
域在AtomicXXX中的内存偏移,这个value必须是volatile
方便更新后内存可见。一般会有如下的代码
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicReference.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
可以看到,后来陆续的原子操作都是基于这个AtomicXXX本体对象(内存基础地址)和valueOffset(偏移地址)定位到变量的绝对地址,直接用底层方法对内存进行各种操作,以AtomicReference为例
//延迟set
public final void lazySet(V newValue) {
unsafe.putOrderedObject(this, valueOffset, newValue);
}
//调用底层方法
public final boolean compareAndSet(V expect, V update) {
return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
}
public final V getAndSet(V newValue) {
return (V)unsafe.getAndSetObject(this, valueOffset, newValue);
}
/**
* 支持UnaryOperator操作符,这个是1.8新加的方法,该Function接收一个Reference类型的
* 入参(也就是old值) 经过该函数操作后获得新值,同时保持操作的原子性
* getAndUpdate和updateAndGet两个方法很有趣,一个返回prev,一个返回next
*/
public final V getAndUpdate(UnaryOperator<V> updateFunction) {
V prev, next;
do {
prev = get();
next = updateFunction.apply(prev);
} while (!compareAndSet(prev, next));
return prev;
}
public final V updateAndGet(UnaryOperator<V> updateFunction) {
V prev, next;
do {
prev = get();
next = updateFunction.apply(prev);
} while (!compareAndSet(prev, next));
return next;
}
/**
* 双目运算符也是可以接受的
**/
public final V getAndAccumulate(V x, BinaryOperator<V> accumulatorFunction) {
V prev, next;
do {
prev = get();
next = accumulatorFunction.apply(prev, x);
} while (!compareAndSet(prev, next));
return prev;
}
public final V accumulateAndGet(V x, BinaryOperator<V> accumulatorFunction) {
V prev, next;
do {
prev = get();
next = accumulatorFunction.apply(prev, x);
} while (!compareAndSet(prev, next));
return next;
}
2.1 原子数组
看完了AtomicXXX之后,AtomicXXXArray类就容易很多了,核心的CAS的套路都是一样的,稍有不同的就是如何获取待修改对象valueOffset
的值,因为不同于AtomicXXX类,数组存放了一堆数据而不是单个,方法在于这个
//数组的基地址,第一个元素的内存位置,除去对象头后的地址
private static final int base = unsafe.arrayBaseOffset(int[].class);
//数组的元素偏移量,取决于什么元素类型的数组,如果是int为4,long为8
private static final int shift;
static {
//对于int来说scale为16
int scale = unsafe.arrayIndexScale(int[].class);
if ((scale & (scale - 1)) != 0)
throw new Error("data type scale not a power of two");
//4位偏移量,即每个元素占据4bit的长度,也就是int的长度
shift = 31 - Integer.numberOfLeadingZeros(scale);
}
//这个是实际计算对于index为i的元素在数组中的内存位置
private long checkedByteOffset(int i) {
if (i < 0 || i >= array.length)
throw new IndexOutOfBoundsException("index " + i);
return byteOffset(i);
}
//base的内存位置+ index * 每个元素的位数(4)= index位置的元素的内存位置
private static long byteOffset(int i) {
return ((long) i << shift) + base;
}
找到index的元素对应的内存位置后,剩下的工作和思路就完全一样的啦!!注意的是数组操作会多一个下标表示操作的元素
public final boolean compareAndSet(int i, int expect, int update) {
return compareAndSetRaw(checkedByteOffset(i), expect, update);
}
public final int getAndUpdate(int i, IntUnaryOperator updateFunction) {
long offset = checkedByteOffset(i);
int prev, next;
do {
prev = getRaw(offset);
next = updateFunction.applyAsInt(prev);
} while (!compareAndSetRaw(offset, prev, next));
return prev;
}
public final int updateAndGet(int i, IntUnaryOperator updateFunction) {
long offset = checkedByteOffset(i);
int prev, next;
do {
prev = getRaw(offset);
next = updateFunction.applyAsInt(prev);
} while (!compareAndSetRaw(offset, prev, next));
return next;
}
3 带计数器的原子类
CAS虽然能够保证最终一致性,但它解决不了ABA问题,特别是对于引用类型,CAS只是保证这个引用句柄(相当于指针)更新是原子的,至于引用从A换成B又换成A,这个过程A指向的对象是不是发生了变化,CAS也无法感知,甚至连ABA这样的变化也感知不到,那么如何响应ABA类型的变化呢,AtomicStampedReference和AtomicMarkableReference为我们带来了答案——既然单个value的持有区分不开ABA场景,那么我们就在这个vaule旁边加一个计数器吧,这样一旦value出现ABA的变更情况,计数器会+2,此时修改后的A和未修改的A就能区分了。
//多了一个内部类,Pair对,分别存储待修改的reference和对此reference修改计数的计数器(戳)
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);
}
}
//对于value从单一的reference变成pair,所有的修改需要同时修改pair(reference + stamp)
private volatile Pair<V> pair;
/**
** compareAndSet除了需要传递待更新的reference,还需要传入正确的stamp
**/
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)));
}
/**
** 计数器的存在使得在使用过程中可能发生漂移(即stamp不知道加到哪里去了),
** 这个时候可以通过这个方法进行校正
**/
public boolean attemptStamp(V expecdReference, int newStamp) {
Pair<V> current = pair;
return
expectedReference == current.reference &&
(newStamp == current.stamp ||
casPair(current, Pair.of(expectedReference, newStamp)));
}
//最后是我们的老朋友unsafe基本类以及获取内存偏移的方法
private static final long pairOffset =
objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class);
private boolean casPair(Pair<V> cmp, Pair<V> val) {
return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}
static long objectFieldOffset(sun.misc.Unsafe UNSAFE, String field, Class<?> klazz) {
try {
return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field));
} catch (NoSuchFieldException e) {
// Convert Exception to corresponding Error
NoSuchFieldError error = new NoSuchFieldError(field);
error.initCause(e);
throw error;
}
}
至此atomic
包的源代码我们已经分析过半,AtomicXXX提供了一套完整的原子变量的方案实现安全并发。可以对于当初程序中写的普通变量,要想实现安全并发的话,改源代码也不好改,同时可能开销也比较大,有其他方法可以实现类似的功能吗,答案是有的,就是下节我们需要介绍的AtomicXXXFieldUpdater类,基于反射的方式实现原子更新。