一、引言
我们先来看一个多线程的运行场景:
时间点1 :线程1查询值是否为A
时间点2 :线程2查询值是否为A
时间点3 :线程2比较并更新值为B
时间点4 :线程2查询值是否为B
时间点5 :线程2比较并更新值为A
时间点6 :线程1比较并更新值为C
在这个线程执行场景中,2个线程交替执行。线程1在时间点6的时候依然能够正常的进行CAS操作,尽管在时间点2到时间点6期间已经发生一些意想不到的变化, 但是线程1对这些变化却一无所知,因为对线程1来说A的确还在。通常将这类现象称为ABA问题。
ABA发生了,但线程不知道。又或者链表的头在变化了两次后恢复了原值,但是不代表链表就没有变化。
二、ABA问题隐患
获取上面的描述ABA问题带来的隐患没有直观的认识,那我们来看下维基百科上面的形象描述:
你拿着一个装满钱的手提箱在飞机场,此时过来了一个火辣性感的美女,然后她很暖昧地挑逗着你,并趁你不注意的时候,把用一个一模一样的手提箱和你那装满钱的箱子调了个包,然后就离开了,你看到你的手提箱还在那,于是就提着手提箱去赶飞机去了。
三、ABA问题解决
1 A a = ref.get(); 2 3 // 根据a的状态做一些操作 4 5 // do something 6 7 // CAS,这时候会出现ABA问题,a指向的对象可能已经变了 8 9 ref.compareAndSet(a, b)
ABA问题我们可以使用JDK的并发包中的AtomicStampedReference和 AtomicMarkableReference来解决。
// 用int做时间戳 AtomicStampedReference<QNode> tail = new AtomicStampedReference<CompositeLock.QNode>(null, 0); int[] currentStamp = new int[1]; // currentStamp中返回了时间戳信息 QNode tailNode = tail.get(currentStamp); tail.compareAndSet(tailNode, null, currentStamp[0], currentStamp[0] + 1)
总结: AtomicStampedReference和 AtomicMarkableReference是通过版本号(时间戳)来解决ABA问题的,我们也可以使用版本号(verison)来解决ABA。
即乐观锁每次在执行数据的修改操作时,都会带上一个版本号,一旦版本号和数据的版本号一致就可以执行修改操作并对版本号执行+1
操作,否则就执行失败。
四、java.util.concurrent.atomic.AtomicStampedReference的源代码是如何实现的
1. 创建一个Pair类来记录对象引用和时间戳信息,采用int作为时间戳,实际使用的时候时间戳信息要做成自增的,否则时间戳如果重复,还会出现ABA的问题。这个Pair对象是不可变对象,所有的属性都是final的, of方法每次返回一个新的不可变对象。
2. 使用一个volatile类型的引用指向当前的Pair对象,一旦volatile引用发生变化,变化对所有线程可见。
3. set方法时,当要设置的对象和当前Pair对象不一样时,新建一个不可变的Pair对象。
4. compareAndSet方法中,只有期望对象的引用和版本号和目标对象的引用和版本好都一样时,才会新建一个Pair对象,然后用新建的Pair对象和原理的Pair对象做CAS操作。
5. 实际的CAS操作比较的是当前的pair对象和新建的pair对象,pair对象封装了引用和时间戳信息。
1 private static class Pair<T> { 2 3 final T reference; 4 5 final int stamp; 6 7 private Pair(T reference, int stamp) { 8 9 this.reference = reference; 10 11 this.stamp = stamp; 12 13 } 14 15 static <T> Pair<T> of(T reference, int stamp) { 16 17 return new Pair<T>(reference, stamp); 18 19 } 20 21 } 22 23 24 25 private volatile Pair<V> pair; 26 27 public AtomicStampedReference(V initialRef, int initialStamp) { 28 29 pair = Pair.of(initialRef, initialStamp); 30 31 } 32 33 34 35 public void set(V newReference, int newStamp) { 36 37 Pair<V> current = pair; 38 39 if (newReference != current.reference || newStamp != current.stamp) 40 41 this.pair = Pair.of(newReference, newStamp); 42 43 } 44 45 46 47 public boolean compareAndSet(V expectedReference, 48 49 V newReference, 50 51 int expectedStamp, 52 53 int newStamp) { 54 55 Pair<V> current = pair; 56 57 return 58 59 expectedReference == current.reference && 60 61 expectedStamp == current.stamp && 62 63 ((newReference == current.reference && 64 65 newStamp == current.stamp) || 66 67 casPair(current, Pair.of(newReference, newStamp))); 68 69 } 70 71 72 73 private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); 74 75 private static final long pairOffset = 76 77 objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class); 78 79 80 81 private boolean casPair(Pair<V> cmp, Pair<V> val) { 82 83 return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val); 84 85 }
相关推荐
CAS下ABA问题及优化方案 CAS(Compare And Set)是一种常见的乐观锁机制,用于降低读写锁冲突,保证数据一致性。但是在极端情况下,CAS 乐观锁机制可能会出现 ABA 问题。本文将从ABA问题的定义、出现原因、优化方案...
3. **ABA问题**:在某些情况下,一个线程读取到的值A,在其他线程中被修改为B,然后再变回A,CAS会认为值没有变化,从而可能导致错误的结果。为了解决这个问题,可以使用版本号或者“戳”来记录变量的状态,如Java中...
- CAS和ABA问题:介绍CAS操作原理和如何解决ABA问题。 6. 热门框架和分布式消息队列 - Spring框架:从Spring到SpringBoot的介绍,讲解框架的使用和原理。 - Mybatis和JDBC:解析数据库操作框架的使用细节和最佳...
总的来说,虽然CAS提供了一种高效的无锁同步机制,但需要开发者注意ABA问题可能带来的潜在风险,并根据具体应用场景选择合适的解决方案。在设计并发程序时,理解CAS的工作原理以及它可能引发的问题是至关重要的,...
CAS(比较并交换)一个小demo import java.util.concurrent.atomic.AtomicInteger; public class CasDemo { public static void main(String[] args) { //默认初始值为5,也就是主存中的值为5 AtomicInteger ...
Java中的ABA问题是指在使用Compare-And-Swap(CAS)操作时可能出现的一种问题。该问题的产生是由于CAS操作的原子性和并发环境中的线程执行顺序的不确定性。ABA问题的出现可能会导致程序的不正确执行和数据的不一致。...
然而,CAS虽然解决了原子性问题,但并不能避免ABA问题。ABA问题是指在并发环境下,一个值从A变到B,然后再变回A,而CAS可能只检查值是否还是A,却忽略了它曾经变为B的过程。这个比喻中,你手中的手提箱虽然看起来...
CAS操作可能会遇到ABA问题(即在两次比较之间,一个值可能被多次修改回到初始状态),这时可以考虑使用带有版本号的CAS或其他替代方案。 #### 七、结论 CAS作为多线程环境中不可或缺的一种同步机制,通过硬件层面...
2. **避免ABA问题**:如果在CAS过程中,旧值A被修改为其他值,然后再变回A,CAS操作会成功,但可能隐藏了错误。解决方法可以引入版本号或者使用更高级的同步原语。 3. **队列扩容**:如果队列接近满,需要动态扩容...
首先是ABA问题,即在CAS操作中,如果一个变量在被读取后、在被写入新值前,其值被修改为另一个值然后再改回原值,CAS操作可能会认为这个变量没有被修改过,导致数据不一致的问题。为了解决ABA问题,Java提供了...
但是,它也有一定的局限性,比如ABA问题(值从A变为B再变回A,但CAS仍然认为没有变化),以及可能导致循环等待(自旋锁)的高CPU占用。因此,在实际使用中,开发者需要根据场景选择合适的同步策略,如使用带有版本号...
- **ABA问题**:当一个值从A变到B,然后再变回A,CAS会认为未发生改变,但实际上值已经被修改过。这是一个潜在的数据一致性风险。 5. **ABA问题的解决** Java通过`AtomicStampedReference`或`...
它通过硬件指令来实现原子操作,提升了并发性能,同时避免了锁带来的开销和死锁问题。 **一、CAS原理** CAS操作包含三个参数:内存位置(V)、预期原值(A)和新值(B)。当且仅当内存位置的当前值等于预期原值时...
在Java编程中,原子操作(Atomic Operation)是...然而,它也存在一定的局限性,如ABA问题和自旋导致的CPU资源浪费。开发者需要根据具体场景选择合适的同步策略,合理利用这些原子操作类,以实现高效且安全的并发代码。
- **ABA问题**:在CAS操作过程中,如果某个值由A变为B再变回A,其他线程可能会误以为该值未被更改。 - **解决方案**: 1. **版本号**:为每个变量添加一个版本号,每次更新变量时都增加版本号,以此区分相同的值。 ...
3. ABA问题:在并发环境下,如果一个值从A变为B,然后再变回A,CAS可能无法检测到中间的变化,因为最终值与预期一致。为解决此问题,可以引入版本号或时间戳,Java提供了`AtomicStampedReference`类,它维护了一个...
同时,ABA也是抗胁迫激素,响应各种生物和非生物胁迫,如:干旱,盐碱,低温冷害等。 茶树作为我国重要的园艺作物之一,是多年生喜温喜湿常绿植物,耐旱性较弱。干旱缺水对茶树的正常生长及茶叶产量、品质有严重的...