CAS 导致 ABA 问题
CAS 算法实现了一个重要的前提,需要取出内存中某时刻的数据,并在当下时刻比较并替换,那么这个时间差会导致数据的变化。
比如说一个线程 one 从内存位置 V 中取出A,这时候另外一个线程 two 也从内存中取出 A,并且线程 two进行了一些操作将值变成了B,然后线程 two 又将 V 位置的数据变成 A,这时候线程 one 进行CAS操作发现内存中仍然是 A,然后线程 one 操作成功 。尽管线程 one 的 CAS 操作成功,但是不代表这个过程就是没有问题的
原子引用
原子引用其实和原子包装类是差不多的概念,就是将一个 java 类,用原子引用类进行包装起来,那么这个类就具备了原子性 。
基于原子引用的 ABA 问题
我们首先创建了两个线程,然后 T1 线程,执行一次 ABA 的操作,T2 线程在一秒后修改主内存的值
运行后成功的修改了,这就是 ABA 问题
解决 ABA 问题
在java的并发包下,有AtomicStampedReference这个类,可以解决ABA问题。其原理其实就是新增一种机制,修改增加版本号,类似于时间戳 的概念
T1: 100 1 2019 2
T2: 100 1 101 2 100 3
如果 T1 修改的时候,版本号为 2,落后于现在的版本号 3,所以要重新获取最新值,这里就提出了一个使用时间戳版本号,来解决 ABA 问题的思路 。
AtomicStampedReference
时间戳原子引用,来这里应用于版本号的更新,也就是每次更新的时候,需要比较期望值和当前值,以及期望版本号和当前版本号 。
我们能够发现,线程 t3,在进行 ABA 操作后,版本号变更成了 3,而线程 t4 在进行操作的时候,就出现操作失败了,因为版本号和当初拿到的不一样 。