1.CAS导致ABA问题:
CAS算法实现一个重要前提需要取出内存中某时刻的数据并在当下时刻比较并交换,那么在这个时间差中会导致数据的变化。
比如:线程1从内存位置V中取出A,这时线程2也从V中取出A,线程2进行了一些操作将值改成了B,然后线程2又将V的数据改回A;此时线程1进行CAS操作发现内存中仍然是A,然后线程1操作成功。
尽管线程1的CAS操作成功,但是不代表这个过程就是没有问题的。
解决ABA问题:利用原子引用+修改版本号(类似时间戳),每次需要获取到版本最新的值进行处理。
2.原子引用AtomicReference
ABA问题出现示例:
package com.mort.test; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; public class TestAtomicReference { static AtomicReference<Integer> atomicReference = new AtomicReference<Integer>(100); public static void main(String[] args) { new Thread(() -> { // 执行ABA操作 atomicReference.compareAndSet(100, 101); atomicReference.compareAndSet(101, 100); }, "t1").start(); new Thread(()->{ // 暂停1秒钟t2线程,保证t1完成了一次ABA操作 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(atomicReference.compareAndSet(100, 2019)+"\t" + atomicReference.get()); }, "t2").start(); } }
3.解决ABA问题:时间戳原子引用AtomicStampedReference
代码示例:
package com.mort.test; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicStampedReference; public class TestABASolve { //static AtomicReference<Integer> atomicReference = new AtomicReference<Integer>(100); static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100,1); public static void main(String[] args) { new Thread(() -> { int stamp = atomicStampedReference.getStamp(); System.out.println(Thread.currentThread().getName()+"\t第1次版本号:"+stamp); // 暂停1秒钟t1线程,保证t2拿到版本号与t1相同 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } // 执行ABA操作 atomicStampedReference.compareAndSet(100, 101,stamp,stamp+1); stamp = atomicStampedReference.getStamp(); System.out.println(Thread.currentThread().getName()+"\t第2次版本号:"+stamp); atomicStampedReference.compareAndSet(101, 100, stamp,stamp+1); // System.out.println(Thread.currentThread().getName()+"\t第3次版本号:"+atomicStampedReference.getStamp()); }, "t1").start(); new Thread(()->{ int stamp = atomicStampedReference.getStamp(); System.out.println(Thread.currentThread().getName()+"\t第1次版本号:"+stamp); // 暂停3秒钟t2线程,保证t1完成了一次ABA操作 try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } boolean result = atomicStampedReference.compareAndSet(100, 2019, stamp, stamp+1); System.out.println(Thread.currentThread().getName()+"\t执行结果:"+result+"\t最新版本号:"+atomicStampedReference.getStamp()); System.out.println(Thread.currentThread().getName()+"\t当前实际最新值:"+atomicStampedReference.getReference()); }, "t2").start(); } }
输出结果:
t1 第1次版本号:1 t2 第1次版本号:1 t1 第2次版本号:2 t2 执行结果:false 最新版本号:3 t2 当前实际最新值:100