基于CAS分析对ABA问题的一点思考
基于CAS分析对ABA问题的一点思考
什么是CAS?
背景
synchronized加锁消耗太大
volatile只保证可见性,不保证原子性
基础
用CPU提供的特殊指令,可以:
- 自动更新共享数据;
- 能检测到是否有其他线程的干扰;
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);
而这里只要一个比较结果,采用标记或摘要的方式明显效率上有优势;
想必AtomicMarkableReference和AtomicStampedReference应该就是出于这样的一种想法设计吧;
基于CAS分析对ABA问题的一点思考的更多相关文章
- AtomicInteger源码分析——基于CAS的乐观锁实现
AtomicInteger源码分析——基于CAS的乐观锁实现 1. 悲观锁与乐观锁 我们都知道,cpu是时分复用的,也就是把cpu的时间片,分配给不同的thread/process轮流执行,时间片与时 ...
- 并发-AtomicInteger源码分析—基于CAS的乐观锁实现
AtomicInteger源码分析—基于CAS的乐观锁实现 参考: http://www.importnew.com/22078.html https://www.cnblogs.com/mantu/ ...
- AtomicInteger源码分析——基于CAS的乐观锁实
1. 悲观锁与乐观锁 我们都知道,cpu是时分复用的,也就是把cpu的时间片,分配给不同的thread/process轮流执行,时间片与时间片之间,需要进行cpu切换,也就是会发生进程的切换.切换涉及 ...
- 集成基于CAS协议的单点登陆
相信大家对单点登陆(SSO,Single Sign On)这个名词并不感到陌生吧?简单地说,单点登陆允许多个应用使用同一个登陆服务.一旦一个用户登陆了一个支持单点登陆的应用,那么在进入其它使用同一单点 ...
- (转)乐观的并发策略——基于CAS的自旋
悲观者与乐观者的做事方式完全不一样,悲观者的人生观是一件事情我必须要百分之百完全控制才会去做,否则就认为这件事情一定会出问题:而乐观者的人生观则相反,凡事不管最终结果如何,他都会先尝试去做,大不了最后 ...
- 乐观的并发策略——基于CAS的自旋
悲观者与乐观者的做事方式完全不一样,悲观者的人生观是一件事情我必须要百分之百完全控制才会去做,否则就认为这件事情一定会出问题:而乐观者的人生观则相反,凡事不管最终结果如何,他都会先尝试去做,大不了最后 ...
- java并发:CAS算法和ABA问题
CAS算法是硬件对于并发的支持,针对多处理器操作而设计的处理器中的一种特殊指令. CAS用于管理对共享数据的并发访问. java的并发包中,AQS.原子操作类等都是基于CAS实现的. CAS 是一种 ...
- Java并发包源码学习系列:基于CAS非阻塞并发队列ConcurrentLinkedQueue源码解析
目录 非阻塞并发队列ConcurrentLinkedQueue概述 结构组成 基本不变式 head的不变式与可变式 tail的不变式与可变式 offer操作 源码解析 图解offer操作 JDK1.6 ...
- CAS如何解决ABA问题
点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人. 文章不定期同步公众号,还有各种一线大厂面试原题.我的学习系列笔记. CAS如何解决ABA问题 什么是ABA:在CAS过程中,线程1.线程2分 ...
随机推荐
- 踩坑 Spring Cloud Hystrix 线程池队列配置
背景: 有一次在生产环境,突然出现了很多笔还款单被挂起,后来排查原因,发现是内部系统调用时出现了Hystrix调用异常.在开发过程中,因为核心线程数设置的比较大,没有出现这种异常.放到了测试环境,偶尔 ...
- TP框架基础(四)----添加数据
[数据添加] add() 该方法返回被添加的新记录的主键id值 两种方式实现数据添加 1. 数组方式数据添加 $goods = D(“Goods”); $arr = array(‘goods_name ...
- linux基础命令期末考试总结
1.关闭防火墙:service iptables stop 2.启动防火墙:service iptables start 3.mount命令:挂载某一设备使之成为某个目录名称 4.NFS服务:linu ...
- Prometheus 整合 AlertManager
简介 Alertmanager 主要用于接收 Prometheus 发送的告警信息,它很容易做到告警信息的去重,降噪,分组,策略路由,是一款前卫的告警通知系统.它支持丰富的告警通知渠道,可以将告警信息 ...
- 1.Java概述、安装及环境搭建
1. 前言 1.1 学习方法 1. 学会学习的方法 2. 学会独立解决问题 3. 主动的学习而不是被动的接受 4. 知识的大家的,能力才是自己的 1.2 推荐博客 当代程序员都应该养成写博客.看博客的 ...
- unc路径
1.什么是UNC路径?UNC路径就是类似\\softer这样的形式的网络路径.UNC为网络(主要指局域网)上资源的完整 Windows 2000 名称.格式: \\servername\sharena ...
- Loadrunner参数(摘)
一.占有率分析 1. 平均事务响应时间 Average Transaction Response Time 优秀:<2s 良好:2-5s 及格:6-10s 不及格:>10s 2. 每秒点击 ...
- 【python-django后端开发】Redis缓存配置使用详细教程!!!
官方查阅资料:https://django-redis-chs.readthedocs.io/zh_CN/latest/ 1. 安装django-redis扩展包 1.安装django-redis扩展 ...
- MySQL 之 Explain 输出分析
MySQL 之 Explain 输出分析 背景 前面的文章写过 MySQL 的事务和锁,这篇文章我们来聊聊 MySQL 的 Explain,估计大家在工作或者面试中多多少少都会接触过这个.可能工作中 ...
- MySQL Schema与数据类型优化
Schema与数据类型优化 选择优化的数据类型 1.更小的通常更好 更小的数据类型通常更快,因为它们占用更少的磁盘,内存和CPU缓存 2.简单就好 简单数据类型的操作通常需要更少的CPU周期.例如:整 ...