CAS和ABA问题
一、引言
我们先来看一个多线程的运行场景:
时间点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问题解决
A a = ref.get(); // 根据a的状态做一些操作 // do something // CAS,这时候会出现ABA问题,a指向的对象可能已经变了 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对象封装了引用和时间戳信息。
private static class Pair<T> { final T reference; final int stamp; private Pair(T reference, int stamp) { this.reference = reference; this.stamp = stamp; } static <T> Pair<T> of(T reference, int stamp) { return new Pair<T>(reference, stamp); } } private volatile Pair<V> pair; public AtomicStampedReference(V initialRef, int initialStamp) { pair = Pair.of(initialRef, initialStamp); } public void set(V newReference, int newStamp) { Pair<V> current = pair; if (newReference != current.reference || newStamp != current.stamp) this.pair = Pair.of(newReference, newStamp); } public 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) || casPair(current, Pair.of(newReference, newStamp))); } private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); private static final long pairOffset = objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class); private boolean casPair(Pair<V> cmp, Pair<V> val) { return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val); }
CAS和ABA问题的更多相关文章
- 沉淀再出发:java中的CAS和ABA问题整理
沉淀再出发:java中的CAS和ABA问题整理 一.前言 在多并发程序设计之中,我们不得不面对并发.互斥.竞争.死锁.资源抢占等等问题,归根到底就是读写的问题,有了读写才有了增删改查,才有了所有的一切 ...
- java并发编程(十三)----(JUC原子类)引用类型介绍(CAS和ABA的介绍)
这一节我们将探讨引用类型原子类:AtomicReference, AtomicStampedRerence, AtomicMarkableReference.AtomicReference的使用非常简 ...
- CAS及其ABA问题
CAS.volatile是JUC包实现同步的基础.Synchronized下的偏向锁.轻量级锁的获取.释放,lock机制下锁的获取.释放,获取失败后线程的入队等操作都是CAS操作锁标志位.state. ...
- Java高性能编程之CAS与ABA及解决方法
Java高性能编程之CAS与ABA及解决方法 前言 如果喜欢暗色调的界面或者想换换界面,可以看看我在个人博客发布的 Java高性能编程之CAS与ABA及解决方法. CAS概念 CAS,全称Compar ...
- CAS的ABA问题详解
CAS的ABA问题详解 ABA问题 在多线程场景下CAS会出现ABA问题,关于ABA问题这里简单科普下,例如有2个线程同时对同一个值(初始值为A)进行CAS操作,这三个线程如下 1.线程1,期望值为A ...
- CAS 和 ABA 问题
CAS简介 CAS 全称是 compare and swap,是一种用于在多线程环境下实现同步功能的机制. CAS 它是一条CPU并发原语.操作包含三个操作数 -- 内存位置.预期数值和新值.CAS ...
- Java并发编程入门与高并发面试(三):线程安全性-原子性-CAS(CAS的ABA问题)
摘要:本文介绍线程的安全性,原子性,java.lang.Number包下的类与CAS操作,synchronized锁,和原子性操作各方法间的对比. 线程安全性 线程安全? 线程安全性? 原子性 Ato ...
- Java CAS 和ABA问题
独占锁:是一种悲观锁,synchronized就是一种独占锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁. 乐观锁:每次不加锁,假设没有冲突去完成某项操作,如果因为冲突失败就重试,直到成功 ...
- 无锁编程(四) - CAS与ABA问题
CAS 一般采用原子级的read-modify-write原语来实现Lock-Free算法,其中LL和SC是Lock-Free理论研究领域的理想原语,但实现这些原语需要CPU指令的支持,非常遗憾的是目 ...
- 谈论高并发(十二)分析java.util.concurrent.atomic.AtomicStampedReference看看如何解决源代码CAS的ABA问题
于谈论高并发(十一)几个自旋锁的实现(五岁以下儿童)中使用了java.util.concurrent.atomic.AtomicStampedReference原子变量指向工作队列的队尾,为何使用At ...
随机推荐
- 18春季训练01-3/11 2015 ACM Amman Collegiate Programming Contest
Solved A Gym 100712A Who Is The Winner Solved B Gym 100712B Rock-Paper-Scissors Solved C Gym 100712C ...
- Mudo C++网络库第二章学习笔记
线程同步的精要 并发有两种基本的模型: 一种是message passing(消息传递); 另一种是shared memory(共享内存); 在分布式系统中(有多台物理机需要通信), 运行在多台机器上 ...
- python操作三大主流数据库(8)python操作mongodb数据库②python使用pymongo操作mongodb的增删改查
python操作mongodb数据库②python使用pymongo操作mongodb的增删改查 文档http://api.mongodb.com/python/current/api/index.h ...
- 使用lld自动发现监控多实例redis
zabbix 可以通过常见的手段监控到各种服务,通过编写脚本来获取返回值并将获取到的值通过图形来展现出来,包括(系统.服务.业务)层面.可是有些时候在一些不固定的场合监控一些不固定的服务就比较麻烦.例 ...
- 快速安装freeswitch
前不久在Centos 6.4上安装了一台Freeswitch,测试已经OK.为了测试FS 的集群效果,从新在安装一台FS,快速安装的过程如下: 方案一:快速安装前提:不用重新下载Freeswitch. ...
- 解决layui选中项下一页清空问题
项目中遇到给用户在所有产品中匹配一部分产品.用layui 第一页选好之后到第二页再选,等回到第一页时之前选择的都没了,解决这个问题的办法如下: //勾选的产品id集合 var chooseAdids ...
- 30个mysql千万级大数据SQL查询优化技巧详解
1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索 ...
- python深浅拷贝与赋值
初学编程的小伙伴都会对于深浅拷贝的用法有些疑问,今天我们就结合python变量存储的特性从内存的角度来谈一谈赋值和深浅拷贝~~~ 预备知识一——python的变量及其存储 在详细的了解python中赋 ...
- Confluence 6 配置 Web 代理支持
这个页面中的相关平台中的内容是不被支持的.因此,Atlassian 支持不能保证能够为你提供任何支持.请注意,这个页面下面提供的信息仅为你提供参考同时也不能保证所有的的配置能正常工作.如果你按照本页面 ...
- Confluence 6 为空白空间重置原始默认内容
希望重置为原始的默认内容: 在屏幕的右上角单击 控制台按钮 ,然后选择 General Configuration 链接. 在左侧的面板中选择 全局模板和蓝图(Global Templates and ...