什么是CAS
CAS:Compare and Swap,即比较再交换。
CAS是一种无锁算法,CAS有三个操作数:
- 内存值V
- 旧的预期值A
- 要修改的新值B
当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
ABA问题的产生
在CAS算法中,需要取出内存中某时刻的数据,在下一刻比较并替换。这个时间差中,会导致数据的变化。
假如以下事件顺序:
线程A:从内存位置V中取出status=A;
线程B:从内存位置V中取出status=A;
线程A:进行一些操作,将改为status=B;
线程A:再次将status改为status=A;
线程B:进行CAS操作,发现内存位置的status还是status=A,操作成功。
尽管线程B的CAS操作成功,但不代表这个过程没问题——对于线程B来说,线程A执行的修改已经丢失。
ABA问题的后果
假设有如下场景:
一个用单向链表实现的栈结构,栈顶元素为A。
这时线程1已经知道A.next为B,然后希望用CAS将栈顶替换为B;
在线程1执行上面这条指令之前,线程2先得到执行,并且线程2将A、B两个元素依次出栈,再将D、C、A元素压入栈中。对象B此时处于游离状态,栈结构如下:
此时轮到线程1进行CAS操作,检测发现栈顶仍然是A,所以CAS成功,栈顶元素变为B,但实际上B.next为null,所以此时的情况变为:栈中只有B一个元素,C和D不再存在于栈中,这样C和D元素就丢了。
ABA问题的解决
AtomicStampedReference
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;
public class AtomicStampedReferenceDemo {
private static AtomicInteger atomicInteger = new AtomicInteger(100);
private static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<Integer>
(100,0);
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(()->{
atomicInteger.compareAndSet(100,101);
atomicInteger.compareAndSet(101,100);
});
Thread thread2 = new Thread(()->{
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean atomicResult = atomicInteger.compareAndSet(100,101);
System.out.println("发生ABA问题时,AtomicInteger执行结果:" + atomicResult);
});
thread1.start();;
thread2.start();;
thread1.join();
thread2.join();
Thread stamped1 = new Thread(()->{
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicStampedReference.compareAndSet(100,101,
atomicStampedReference.getStamp(),atomicStampedReference.getStamp() + 1);
atomicStampedReference.compareAndSet(101,100,
atomicStampedReference.getStamp(),atomicStampedReference.getStamp() + 1);
System.out.println("线程stamped1获取的版本号 = " + atomicStampedReference.getStamp());
});
Thread stamped2 = new Thread(()->{
int stamp = atomicStampedReference.getStamp();
System.out.println("线程stamped2在ABA发生前获取的版本号 = " + atomicStampedReference.getStamp());
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean atomicSyampedResult = atomicStampedReference.compareAndSet(100,101,
stamp,stamp + 1);
System.out.println("发生ABA问题时,AtomicStampedReference执行结果:" + atomicSyampedResult);
});
stamped1.start();
stamped2.start();
}
}
AtomicMarkableReference
import java.util.concurrent.atomic.AtomicMarkableReference;
public class AtomicMarkableReferenceDemo {
/**
* 初始值
*/
private static final Integer INIT_NUM = 10;
/**
* 临时值
*/
private static final Integer TEMP_NUM = 20;
/**
* 更新值
*/
private static final Integer UPDATE_NUM = 100;
private static final Boolean INITIAL_MARK = Boolean.FALSE;
private static AtomicMarkableReference atomicMarkableReference = new AtomicMarkableReference(INIT_NUM,INITIAL_MARK);
public static void main(String[] args) {
new Thread(()->{
System.out.println(Thread.currentThread().getName() + " :初始值为:" + INIT_NUM + " ,标记为:" + INITIAL_MARK);
boolean mark = atomicMarkableReference.isMarked();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean result = atomicMarkableReference.compareAndSet(INIT_NUM,UPDATE_NUM,mark,Boolean.TRUE);
System.out.println("atomicMarkableReference发生ABA后的执行结果 = " + result);
},"线程A").start();
new Thread(()->{
Thread.yield();
System.out.println(Thread.currentThread().getName() + " :初始值为:" + INIT_NUM + " ,标记为:" + INITIAL_MARK);
atomicMarkableReference.compareAndSet(atomicMarkableReference.getReference(),TEMP_NUM,
atomicMarkableReference.isMarked(),Boolean.TRUE);
System.out.println(Thread.currentThread().getName() + " :第一次修改后的值为:"
+ atomicMarkableReference.getReference() + " ,标记为:" + INITIAL_MARK);
atomicMarkableReference.compareAndSet(atomicMarkableReference.getReference(),INIT_NUM,
atomicMarkableReference.isMarked(),Boolean.TRUE);
System.out.println(Thread.currentThread().getName() + " :第二次修改后的值为:"
+ atomicMarkableReference.getReference() + " ,标记为:" + INITIAL_MARK);
},"线程B").start();
}
}
源码分析
AtomicStampedReference源码
public class AtomicStampedReference<V> {
//Pair对象维护对象的引用和对象标志的版本,通过Pair对象解决ABA问题
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);
}
}
private volatile Pair<V> pair;
public AtomicStampedReference(V initialRef, int initialStamp) {
pair = Pair.of(initialRef, initialStamp);
}
public V getReference() {
return pair.reference;
}
public int getStamp() {
return pair.stamp;
}
public V get(int[] stampHolder) {
Pair<V> pair = this.pair;
stampHolder[0] = pair.stamp;
return pair.reference;
}
public boolean weakCompareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
return compareAndSet(expectedReference, newReference,
expectedStamp, newStamp);
}
/**
* @param expectedReference 期待的原始对象
* @param newReference 将要更新的对象
* @param expectedStamp 期待原始对象的标志版本
* @param newStamp 将要更新对象的标志版本
*/
public boolean compareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
Pair<V> current = pair;
return
//如果返回true则说明期待的原始对象与Pair的reference对象一样
expectedReference == current.reference &&
//如果返回true说明期待原始对象标志版本与Pair的stamp对象一样
expectedStamp == current.stamp &&
//如果期待更新的对象和标志版本与Pair的reference
//和stamp一样直接返回true
//否则执行CAS
((newReference == current.reference &&
newStamp == current.stamp) ||
casPair(current, Pair.of(newReference, newStamp)));
}
public void set(V newReference, int newStamp) {
Pair<V> current = pair;
if (newReference != current.reference || newStamp != current.stamp)
this.pair = Pair.of(newReference, newStamp);
}
public boolean attemptStamp(V expectedReference, int newStamp) {
Pair<V> current = pair;
return
expectedReference == current.reference &&
(newStamp == current.stamp ||
casPair(current, Pair.of(expectedReference, newStamp)));
}
private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
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;
}
}
}
AtomicMarkableReference源码
Pair对象维护对象的引用和对象标记
private static class Pair<T> {
final T reference;
//通过标记的状态区分对象是否有更改
final boolean mark;
private Pair(T reference, boolean mark) {
this.reference = reference;
this.mark = mark;
}
static <T> Pair<T> of(T reference, boolean mark) {
return new Pair<T>(reference, mark);
}
}
/**
* @param expectedReference 期待的原始对象
* @param newReference 将要更新的对象
* @param expectedStamp 期待原始对象的标记
* @param newStamp 将要更新对象的标记
*/
public boolean compareAndSet(V expectedReference,
V newReference,
boolean expectedMark,
boolean newMark) {
Pair<V> current = pair;
return
//如果期待的原始对象与Pair的reference一样则返回true
expectedReference == current.reference &&
//如果期待的原始对象标记与Pair的mark一样则返回true
expectedMark == current.mark &&
//如果要更新的对象和对象标记
//与Pair的reference和mark一样的话直接返回true
//否则执行CAS操作
((newReference == current.reference &&
newMark == current.mark) ||
casPair(current, Pair.of(newReference, newMark)));
}