基于CAS分析对ABA问题的一点思考

什么是CAS?

背景

synchronized加锁消耗太大

volatile只保证可见性,不保证原子性

基础

用CPU提供的特殊指令,可以:

  1. 自动更新共享数据;
  2. 能检测到是否有其他线程的干扰;

CAS(Compare and Swap)

不加锁而是尝试去完成替换(写)操作,如果失败就重试,直到成功;

分析

// AtomicInteger
public class AtomicInteger extends Number implements java.io.Serializable {
private static final Unsafe unsafe = Unsafe.getUnsafe(); // 直接操作内存的底层类
private static final long valueOffset; // value属性的内存位置
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
} private volatile int value; // 用volatile保证可见性 public final int addAndGet(int delta) { // 更新操作入口
return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
} ... } // Unsafe
public final class Unsafe {
// 添加操作
public final int getAndAddInt(Object obj, long valueOffset, int delta) {
int value;
do {
value = this.getIntVolatile(obj, valueOffset); // 取出最新值
// 一直尝试替换操作,直到成功替换
} while(!this.compareAndSwapInt(obj, valueOffset, value, value + delta));
return value;
} // 本地的比较方法
public final native boolean compareAndSwapInt(Object obj, long valueOffset, int expect, int update); }

总结

比之于悲观的加锁阻塞,乐观的CAS算法是非阻塞的. J.U.C(java.util.concurrent)是建立在CAS之上的,所以在性能上有很大的优势;

什么是ABA问题

维基百科对ABA的说明很形象,大意是:

你提着很多现金的包去机场,这时来了个辣妹挑逗你,并趁你不注意时用一个一模一样的空包换了你的现金包,然后她就走了,此时你发现你的包还在,于是就是拿着包去赶飞机了.

注意几个关键字眼: 现金包, 辣妹, 空包, 一模一样, 发现包还在;

翻译成代码

有链表: A->B->NULL;

此时线程1想移除A把表头替换成B: list.compareAndSet(A,B);

但是还没执行,线程2抢占了时间片,它把B移除了并添加了节点C。

链表: A->C->null, 此时B游离: B->null;

这时又轮到线程1执行了,检查发现list表头还是A,所以进行替换操作。这样做就导致了C节点数据丢失。

分析

这里不论是皮包还是节点,都有一个特点: 非基本数据类型, 即壳的内部还有数据;

辣妹就是抢占了时间片的线程, "一模一样"和"发现包还在"都是只检查了壳而并没有检查内部数据,所以导致ABA问题,丢了C节点;

// Node
public static class Node {
char value;
Node next; public Node(char value) {
this.value = value;
} @Override
public boolean equals(Object obj) {
System.out.println("equals"); // compareAndSet是native的并没有走equals
return super.equals(obj);
}
} // test
public static void main(String... arg) throws InterruptedException {
Node a = new Node('A');
Node b = new Node('B');
Node c = new Node('C'); a.next = b;
AtomicReference<Node> stack = new AtomicReference<>(a); a.next = c; // 改变了内部数据,但是比较时任然认为A还是原来的A
boolean x = stack.compareAndSet(a, b); System.out.println(x); // x == true
}

个人感觉解决了equals的问题上层就可控制ABA问题了。

这就和比较两个文件一样: 可以根据内容逐行扫描(equals),也可以摘要比较(如文件MD5);

而这里只要一个比较结果,采用标记或摘要的方式明显效率上有优势;

想必AtomicMarkableReferenceAtomicStampedReference应该就是出于这样的一种想法设计吧;

基于CAS分析对ABA问题的一点思考的更多相关文章

  1. AtomicInteger源码分析——基于CAS的乐观锁实现

    AtomicInteger源码分析——基于CAS的乐观锁实现 1. 悲观锁与乐观锁 我们都知道,cpu是时分复用的,也就是把cpu的时间片,分配给不同的thread/process轮流执行,时间片与时 ...

  2. 并发-AtomicInteger源码分析—基于CAS的乐观锁实现

    AtomicInteger源码分析—基于CAS的乐观锁实现 参考: http://www.importnew.com/22078.html https://www.cnblogs.com/mantu/ ...

  3. AtomicInteger源码分析——基于CAS的乐观锁实

    1. 悲观锁与乐观锁 我们都知道,cpu是时分复用的,也就是把cpu的时间片,分配给不同的thread/process轮流执行,时间片与时间片之间,需要进行cpu切换,也就是会发生进程的切换.切换涉及 ...

  4. 集成基于CAS协议的单点登陆

    相信大家对单点登陆(SSO,Single Sign On)这个名词并不感到陌生吧?简单地说,单点登陆允许多个应用使用同一个登陆服务.一旦一个用户登陆了一个支持单点登陆的应用,那么在进入其它使用同一单点 ...

  5. (转)乐观的并发策略——基于CAS的自旋

    悲观者与乐观者的做事方式完全不一样,悲观者的人生观是一件事情我必须要百分之百完全控制才会去做,否则就认为这件事情一定会出问题:而乐观者的人生观则相反,凡事不管最终结果如何,他都会先尝试去做,大不了最后 ...

  6. 乐观的并发策略——基于CAS的自旋

    悲观者与乐观者的做事方式完全不一样,悲观者的人生观是一件事情我必须要百分之百完全控制才会去做,否则就认为这件事情一定会出问题:而乐观者的人生观则相反,凡事不管最终结果如何,他都会先尝试去做,大不了最后 ...

  7. java并发:CAS算法和ABA问题

    CAS算法是硬件对于并发的支持,针对多处理器操作而设计的处理器中的一种特殊指令. CAS用于管理对共享数据的并发访问. java的并发包中,AQS.原子操作类等都是基于CAS实现的. CAS 是一种 ...

  8. Java并发包源码学习系列:基于CAS非阻塞并发队列ConcurrentLinkedQueue源码解析

    目录 非阻塞并发队列ConcurrentLinkedQueue概述 结构组成 基本不变式 head的不变式与可变式 tail的不变式与可变式 offer操作 源码解析 图解offer操作 JDK1.6 ...

  9. CAS如何解决ABA问题

    点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人. 文章不定期同步公众号,还有各种一线大厂面试原题.我的学习系列笔记. CAS如何解决ABA问题 什么是ABA:在CAS过程中,线程1.线程2分 ...

随机推荐

  1. springboot+mongodb 按日期分组分页查询

    List<Integer> types = new ArrayList<>(); types.add("条件1"); types.add("条件2 ...

  2. arm汇编指令--str ldr

    STR :把寄存器中的字保存到存储器(寄存器到存储器) 示例: STR R0,[R1],#8             :将R0中的字数据写入以R1为地址的存储器中,并将新地址R1+8写入R1.STR ...

  3. jsp数据交互(一).1

    一.jsp中java小脚本1.<% java代码段%>2.<% =java表达式%>不能有分号3.<%!成员变量和函数声明%>二.注释1.<!--html注释 ...

  4. javaweb入门---web服务器与HTTP协议基础

    上文web基础简介了web到底是什么,以及身为Java开发人员需要掌握的地方.本文将解答web服务器是什么,怎么使用?还有关于http协议的基础知识. web服务器 web服务器的大概念很广泛,但是通 ...

  5. 关于报错:The Microsoft.ACE. Oledb.12.0 provider was not registered on the local computer

    错误描述:The Microsoft.ACE. Oledb.12.0 provider was not registered on the local computer 最近在Web项目中做一个自动生 ...

  6. 利用模板生成html页面(NVelocity)

    公司的网站需要有些新闻,每次的新闻格式都是一样的,而不想每次都查询操作,所以想把这些新闻的页面保存成静态的html,之后搜索了下就找到了这个模板引擎,当然其他的模板引擎可以的,例如:Razor,自己写 ...

  7. DesignPattern系列__06迪米特原则

    迪米特原则定义 迪米特原则,也叫最少知道原则,即一个类应该对自己依赖的类知道的越少越好,而你被依赖的类多么复杂,对我都没有关系.也就是说,对于别依赖的类来说,不管业务逻辑多么复杂,都应该尽量封装在类的 ...

  8. 洛谷P2630 题解

    我先讲一下我的思路 将A,B,C,D四种操作用函数储存起来: 枚举所有可能出现的情况:A,B,C,D,AA,AB,AC,AD,BB,BC,BD,CC,CD,DD,ABC,ABD,ACD,BCD,ABC ...

  9. java高并发系列 - 第23天:JUC中原子类,一篇就够了

    这是java高并发系列第23篇文章,环境:jdk1.8. 本文主要内容 JUC中的原子类介绍 介绍基本类型原子类 介绍数组类型原子类 介绍引用类型原子类 介绍对象属性修改相关原子类 预备知识 JUC中 ...

  10. swift 分享share页面封装(功能按钮不同)

    关于分享功能的页面应该有很多,写这篇swift版本的分享页面,根据不同模块可能分享的功能按钮不一样,引言: 想必大家都使用微博右上角更多按钮,会弹出如下的界面: 在开发中,可能针对同一个app的不同按 ...