转自(here)   CAS问题的产生

在运用CAS做Lock-Free操作中有一个经典的ABA问题:

线程1准备用CAS将变量的值由A替换为B,在此之前,线程2将变量的值由A替换为C,又由C替换为A,然后线程1执行CAS时发现变量的值仍然为A,所以CAS成功。但实际上这时的现场已经和最初不同了,尽管CAS成功,但可能存在潜藏的问题,例如下面的例子:

现有一个用单向链表实现的堆栈,栈顶为A,这时线程T1已经知道A.next为B,然后希望用CAS将栈顶替换为B:

head.compareAndSet(A,B);

在T1执行上面这条指令之前,线程T2介入,将A、B出栈,再pushD、C、A,此时堆栈结构如下图,而对象B此时处于游离状态:

此时轮到线程T1执行CAS操作,检测发现栈顶仍为A,所以CAS成功,栈顶变为B,但实际上B.next为null,所以此时的情况变为:

其中堆栈中只有B一个元素,C和D组成的链表不再存在于堆栈中,平白无故就把C、D丢掉了。

以上就是由于ABA问题带来的隐患,各种乐观锁的实现中通常都会用版本戳version来对记录或对象标记,避免并发操作带来的问题,在Java中,AtomicStampedReference<E>也实现了这个作用,它通过包装[E,Integer]的元组来对对象标记版本戳stamp,从而避免ABA问题,例如下面的代码分别用AtomicInteger和AtomicStampedReference来对初始值为100的原子整型变量进行更新,AtomicInteger会成功执行CAS操作,而加上版本戳的AtomicStampedReference对于ABA问题会执行CAS失败:

public class Test {
private static AtomicInteger atomicInt = new AtomicInteger(100);
private static AtomicStampedReference atomicStampedRef = new AtomicStampedReference(100, 0); public static void main(String[] args) throws InterruptedException {
Thread intT1 = new Thread(new Runnable() {
@Override
public void run() {
atomicInt.compareAndSet(100, 101);
atomicInt.compareAndSet(101, 100);
}
}); Thread intT2 = new Thread(new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
boolean c3 = atomicInt.compareAndSet(100, 101);
System.out.println(c3); // true
}
}); intT1.start();
intT2.start();
intT1.join();
intT2.join(); Thread refT1 = new Thread(new Runnable() {
@Override
public void run()
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
atomicStampedRef.compareAndSet(100, 101, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1);
atomicStampedRef.compareAndSet(101, 100, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1);
}
}); Thread refT2 = new Thread(new Runnable() {
@Override
public void run() {
int stamp = atomicStampedRef.getStamp();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
}
boolean c3 = atomicStampedRef.compareAndSet(100, 101, stamp, stamp + 1);
System.out.println(c3); // false
}
}); refT1.start();
refT2.start();
}

PS: AtomicStampedReference解决了CAS的ABA问题,其实是在出现ABA问题的时候执行不成功,而不是自动纠正这个问题.
												

CAS中ABA问题的解决的更多相关文章

  1. 谈论高并发(十二)分析java.util.concurrent.atomic.AtomicStampedReference看看如何解决源代码CAS的ABA问题

    于谈论高并发(十一)几个自旋锁的实现(五岁以下儿童)中使用了java.util.concurrent.atomic.AtomicStampedReference原子变量指向工作队列的队尾,为何使用At ...

  2. CAS与ABA问题产生和解决

    乐观锁和悲观锁 Synchronized属于悲观锁,悲观地认为程序中的并发情况严重,所以严防死守.CAS属于乐观锁,乐观地认为程序中的并发情况不那么严重,所以让线程不断去尝试更新. 性能对比: Syn ...

  3. 沉淀再出发:java中的CAS和ABA问题整理

    沉淀再出发:java中的CAS和ABA问题整理 一.前言 在多并发程序设计之中,我们不得不面对并发.互斥.竞争.死锁.资源抢占等等问题,归根到底就是读写的问题,有了读写才有了增删改查,才有了所有的一切 ...

  4. Java高性能编程之CAS与ABA及解决方法

    Java高性能编程之CAS与ABA及解决方法 前言 如果喜欢暗色调的界面或者想换换界面,可以看看我在个人博客发布的 Java高性能编程之CAS与ABA及解决方法. CAS概念 CAS,全称Compar ...

  5. CAS导致的ABA问题及解决:时间戳原子引用AtomicReference、AtomicStampedReference

    1.CAS导致ABA问题: CAS算法实现一个重要前提需要取出内存中某时刻的数据并在当下时刻比较并交换,那么在这个时间差中会导致数据的变化. 比如:线程1从内存位置V中取出A,这时线程2也从V中取出A ...

  6. CAS中的ABA问题

    http://coolshell.cn/articles/8239.html CAS的ABA问题 所谓ABA(见维基百科的ABA词条),问题基本是这个样子: 进程P1在共享变量中读到值为A P1被抢占 ...

  7. 【Java并发编程】2、无锁编程:lock-free原理;CAS;ABA问题

    转自:http://blog.csdn.net/kangroger/article/details/47867269 定义 无锁编程是指在不使用锁的情况下,在多线程环境下实现多变量的同步.即在没有线程 ...

  8. java并发编程(十三)----(JUC原子类)引用类型介绍(CAS和ABA的介绍)

    这一节我们将探讨引用类型原子类:AtomicReference, AtomicStampedRerence, AtomicMarkableReference.AtomicReference的使用非常简 ...

  9. CAS及其ABA问题

    CAS.volatile是JUC包实现同步的基础.Synchronized下的偏向锁.轻量级锁的获取.释放,lock机制下锁的获取.释放,获取失败后线程的入队等操作都是CAS操作锁标志位.state. ...

  10. CAS的ABA问题详解

    CAS的ABA问题详解 ABA问题 在多线程场景下CAS会出现ABA问题,关于ABA问题这里简单科普下,例如有2个线程同时对同一个值(初始值为A)进行CAS操作,这三个线程如下 1.线程1,期望值为A ...

随机推荐

  1. DIY制作隔离信号注入变压器

    最近在学习模电知识,接触到了测量运放环路增益,需要使用合适的注入变压器,查找资料发现商用信号注入变压器价格昂贵,不适合个人学习使用.看到LOTO使用普通音频变压器做测试,也跟技术群友做了交流,尝试使用 ...

  2. 微信公众号redirect_uri 参数错误

    前期所有准备工作我就不在这里一一叙述了.在这说一下需要注意的事项: 1.如果域名为www开头,记得把www去掉,否则依旧会报这个错误 2.跳转域名必须是授权域名的子集,如:'授权域名为 www.bai ...

  3. 今天做php缓存发现一个批量替换非法关键词的php代码

    <?php $badwords = array('aa', 'bb'); // 敏感词数组 $str = "aa阿斯顿克拉拉斯柯达bb阿萨达速度"; $result = fi ...

  4. java.lang.reflect.UndeclaredThrowableException

    org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.Persiste ...

  5. 详解nvim内建LSP体系与基于nvim-cmp的代码补全体系

    2023年,nvim以及其生态已经发展的愈来愈完善了.nvim内置的LSP(以及具体的语言服务)加上众多插件,可以搭建出支持各种类型语法检查.代码补全.代码格式化等功能的IDE.网络上关于如何配置的文 ...

  6. Blazor前后端框架Known-V1.2.4

    V1.2.4 Known是基于C#和Blazor开发的前后端分离快速开发框架,开箱即用,跨平台,一处代码,多处运行. Gitee: https://gitee.com/known/Known Gith ...

  7. linux内核编译基础知识储备 --- 过渡篇(四)

    文章目录 一. 一种makefile中定义函数的方式 二. makefile之if函数 三. MAKE变量 四. $(quiet) 4.1 quiet = 空 与 quiet=quiet_ 的区别 4 ...

  8. [Spring+SpringMVC+Mybatis]框架学习笔记(八):Mybatis概述

    第8章 Mybatis概述 8.1 几个概念 ORM Object-Relationship Mapping 对象关系映射,它是一种思想,它的实质是将数据库中的数据用对象的形式表现出来. JPA Ja ...

  9. Redis核心技术与实践 03 | 高性能IO模型:为什么单线程Redis能那么快?

    原文地址:https://time.geekbang.org/column/article/268262 个人博客地址:http://njpkhuan.cn/archives/redis-he-xin ...

  10. vim玩法 .vimrc配置映射指令nnoremap、inoremap

    编辑 vimrc 文件, vi ~/.vimrc vim中的映射指令,用于将一个按键绑定到某一个操作上. map: 执行映射指令,执行时会进行递归替换,可能会出现"按键循环"的情况 ...