CAS算法中的问题
CAS算法中的问题
- CAS算法中的问题主要有以下三点:
- ABA问题
- 循环时间长开销大
- 只能保证一个共享变量的原子操作
1. ABA问题
- ABA问题:一个变量V初次读取时为A,准备赋值时检查仍未A,但不能说明它没有被其他线程修改过(可能被修改为其他值后又被改回来了)。
- ABA问题的解决思路就是在变量前面追加上版本号或时间戳。JDK1.5之后的
AutomicStampedReference类就是用来解决ABA问题的,其中的compareAndSet()方法会同时比较引用值和标志,只有两个都相等时才会更新引用值和标志1
2
3
4
5
6
7
8
9
10
11
12public 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) || // 如果新值和旧值相等则直接返回true,不CAS
casPair(current, Pair.of(newReference, newStamp))); //如果新值和旧值不同,执行CAS
}
2. 循环时间长开销大
- CAS经常会用到自旋操作来进行重试,也就是不成功就一直循环执行直到成功。如果长时间不成功,就会增加非常大的CPU的开销,影响系统性能。
pause指令可以提升自旋操作的效率,其作用:- 延迟流水线执行指令:
pause指令可以延迟指令的执行,从而减少CPU的资源消耗。具体延迟时间取决于处理器的实现版本 - 避免内存顺序冲突:在退出循环时,
pause指令可以避免由于内存顺序冲突而导致的CPU流水线被清空,从而提高CPU的执行效率
- 延迟流水线执行指令:
3. 只能保证一个共享变量的原子操作
- CAS操作仅能对单个共享变量有效。
- 当需要操作多个共享变量时,CAS就显得无能为力。那么如何解决呢?
- 从JDK1.5开始,Java提供了
AtomicReference类,可以保证引用对象之间的原子性。通过将多个变量封装在一个对象中后可以使用AtomicReference来执行CAS操作。
- 从JDK1.5开始,Java提供了
- 除了
AtomicReference这种方式之外,还可以利用加锁来保证。
