补档CAS中的ABA问题。java
要特别注意,常见的ABA问题有两种,要求能分别举例解释。node
CAS的使用可参考:git
在CAS算法中,须要取出内存中某时刻的数据(由用户完成),在下一时刻比较并替换(由CPU完成,该操做是原子的)。这个时间差中,会致使数据的变化。程序员
假设以下事件序列:github
尽管线程 1 的CAS操做成功,但不表明这个过程没有问题——对于线程 1 ,线程 2 的修改已经丢失。算法
在没有垃圾回收机制的内存模型中(如C++),程序员可随意释放内存。并发
假设以下事件序列:spa
这里比问题 1.1 的后果更严重,实际内容已经被修改了,但_线程 1 没法感知到线程 2 的修改_。线程
更甚,若是线程 2 只释放了A指向的内存,而线程 1 在 CAS以前还要访问A中的内容,那么线程 1 将访问到一个野指针
。指针
若是位置V存储的是链表的头结点,那么发生ABA问题的链表中,原头结点是node1,线程 2 操做头结点变化了两次,极可能是先修改头结点为node2,再将node1(在C++中,也但是从新分配的节点node3,但刚好其指针等于已经释放掉的node1)插入表头成为新的头结点。
对于线程 1 ,头结点仍旧为 node1(或者说头结点的值,由于在C++中,虽然地址相同,但其内容可能变为了node3),CAS操做成功,但头结点以后的子链表的状态已不可预知。
脑补示意图。。
问题定义已阐述清楚。
Java的垃圾回收机制已经帮咱们解决了问题 1.2;至于问题 1.1,加入版本号便可解决。
除了对象值,AtomicStampedReference内部还维护了一个“状态戳
”。状态戳可类比为时间戳,是一个整数值,每一次修改对象值的同时,也要修改状态戳,从而区分相同对象值的不一样状态。当AtomicStampedReference设置对象值时,对象值以及状态戳都必须知足指望值,写入才会成功。
AtomicStampedReference的几个API在AtomicReference的基础上新增了有关时间戳的信息:
//比较设置 参数依次为:指望值 写入新值 指望时间戳 新时间戳
public boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp) //得到当前对象引用 public V getReference() //得到当前时间戳 public int getStamp() //设置当前对象引用和时间戳 public void set(V newReference, int newStamp) 复制代码
AtomicMarkableReference和AtomicStampedReference功能类似,但AtomicMarkableReference描述更加简单的是与否的关系。它的定义就是将状态戳简化为true|false
。以下:
public final static AtomicMarkableReference<String> ATOMIC_MARKABLE_REFERENCE
= new AtomicMarkableReference<>("abc" , false);
复制代码
操做时:
ATOMIC_MARKABLE_REFERENCE.compareAndSet("abc", "abc2", false, true);
复制代码
本文连接:CAS中的ABA问题
做者:猴子007
出处:monkeysayhi.github.io
本文基于 知识共享署名-相同方式共享 4.0 国际许可协议发布,欢迎转载,演绎或用于商业目的,可是必须保留本文的署名及连接。