目录
目标
了解concurrent包前,需要先认识cas和volatile。
JDK版本
JDK8
基础知识
volatile关键字
保证内存可见性,写操作 happen-before 读操作。强制线程不缓存,每次均使用主存最新数据。
cas
api如unsafe.compareAndSwapXXX,由jvm提供实现,依赖处理器的原子性指令。
sun.misc.Unsafe 单例模式
使用举例,以AtomicReference为例
private static final Unsafe unsafe = Unsafe.getUnsafe();//获取单例
private static final long valueOffset;//field相对偏移量
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicReference.class.getDeclaredField("value"));//获取偏移量
} catch (Exception ex) { throw new Error(ex); }
}
private volatile V value;//上述指的field
cas(A,B,C,D)参数说明
A | field所在对象 |
B | field相对于对象内存空间首地址的相对偏移量 |
C | 期待的数值 |
D | 新数值 |
ABA问题
问题描述
一个线程1使用cas将数据从A改为B前,另一个线程使用cas将数据从A改为D然后又改为A,虽然线程1能执行成功,但是实际情况可能不符合预期结果。
解决方案
使用版本解决ABA问题,AtomicStampedReference
基于Pair模型
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逻辑,对比期待数据,设置新数据
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))); // 设置新数据
}
更多关于concurrent包的原理,请前往我的其他文章。