文档章节

CAS原理分析及ABA问题详解

Onegoleya
 Onegoleya
发布于 02/28 19:41
字数 2074
阅读 171
收藏 0

3 月,跳不动了?>>>

什么是CAS

CASCompare And Swap的缩写,翻译成中文就是比较并交换,其作用是让CPU比较内存中某个值是否和预期的值相同,如果相同则将这个值更新为新值,不相同则不做更新,也就是CAS是原子性的操作(读和写两者同时具有原子性),其实现方式是通过借助C/C++调用CPU指令完成的,所以效率很高。 CAS的原理很简单,这里使用一段Java代码来描述

public boolean compareAndSwap(int value, int expect, int update) {
//        如果内存中的值value和期望值expect一样 则将值更新为新值update
    if (value == expect) {
        value = update;
        return true;
    } else {
        return false;
    }
}

大致过程是将内存中的值、我们的期望值、新值交给CPU进行运算,如果内存中的值和我们的期望值相同则将值更新为新值,否则不做任何操作。这个过程是在CPU中完成的,这里不好描述CPU的工作过程,就拿Java代码来描述了。

Unsafe源码分析

Java是在Unsafe(sun.misc.Unsafe)类实现CAS的操作,而我们知道Java是无法直接访问操作系统底层的API的(原因是Java的跨平台性限制了Java不能和操作系统耦合),所以Java并没有在Unsafe类直接实现CAS的操作,而是通过**JDI(Java Native Interface)**本地调用C/C++语言来实现CAS操作的。 Unsafe有很多个CAS操作的相关方法,这里举例几个

public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);

public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

我们拿public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);进行分析,这个方法是比较内存中的一个值(整型)和我们的期望值(var4)是否一样,如果一样则将内存中的这个值更新为var5,参数中的var1是值所在的对象,var2是值在对象(var1)中的内存偏移量,参数var1和参数var2是为了定位出值所在内存的地址http://static.cyblogs.com/e0e01e43ly1g11dbvrjwoj21ay0bujtl.jpg Unsafe.java在这里发挥的作用有:

  1. 将对象引用、值在对象中的偏移量、期望的值和欲更新的新值传递给Unsafe.cpp
  2. 如果值更新成功则返回true给开发者,没有更新则返回false

Unsafe.cpp在这里发挥的作用有:

  1. 接受从Unsafe传递过来的对象引用、偏移量、期望的值和欲更新的新值,根据对象引用和偏移量计算出值的地址,然后将值的地址、期望的值、欲更新的新值传递给CPU
  2. 如果值更新成功则返回trueUnsafe.java,没有更新则返回false

CPU在这里发挥的作用:

  1. 接受从Unsafe.cpp传递过来的地址、期望的值和欲更新的新值,执行指令cmpxchg,比较地址中的值是否和期望的值一样,一样则将值更新为新的值,不一样则不做任何操作
  2. 将操作结果返回给Unsafe.cpp

CAS的缺点

CAS虽然高效的实现了原子性操作,但是也存在一些缺点,主要表现在以下三个方面。

ABA问题

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

  1. 线程1,期望值为A,欲更新的值为B
  2. 线程2,期望值为A,欲更新的值为B

线程1抢先获得CPU时间片,而线程2因为其他原因阻塞了,线程1取值与期望的A值比较,发现相等然后将值更新为B,然后这个时候出现了线程3,期望值为B,欲更新的值为A,线程3取值与期望的值B比较,发现相等则将值更新为A,此时线程2从阻塞中恢复,并且获得了CPU时间片,这时候线程2取值与期望的值A比较,发现相等则将值更新为B,虽然线程2也完成了操作,但是线程2并不知道值已经经过了A->B->A的变化过程。

ABA问题带来的危害: 小明在提款机,提取了50元,因为提款机问题,有两个线程,同时把余额从100变为50 线程1(提款机):获取当前值100,期望更新为50, 线程2(提款机):获取当前值100,期望更新为50, 线程1成功执行,线程2某种原因block了,这时,某人给小明汇款50 线程3(默认):获取当前值50,期望更新为100, 这时候线程3成功执行,余额变为100, 线程2从Block中恢复,获取到的也是100,compare之后,继续更新余额为50!!! 此时可以看到,实际余额应该为100(100-50+50),但是实际上变为了50(100-50+50-50)这就是ABA问题带来的成功提交。

解决方法: 在变量前面加上版本号,每次变量更新的时候变量的版本号都+1,即A->B->A就变成了1A->2B->3A

循环时间长开销大

如果CAS操作失败,就需要循环进行CAS操作(循环同时将期望值更新为最新的),如果长时间都不成功的话,那么会造成CPU极大的开销。

这种循环也称为自旋

解决方法: 限制自旋次数,防止进入死循环。

只能保证一个共享变量的原子操作

CAS的原子操作只能针对一个共享变量。

解决方法: 如果需要对多个共享变量进行操作,可以使用加锁方式(悲观锁)保证原子性,或者可以把多个共享变量合并成一个共享变量进行CAS操作。

CAS的应用

我们知道CAS操作并不会锁住共享变量,也就是一种非阻塞的同步机制,CAS就是乐观锁的实现。

  1. 乐观锁 乐观锁总是假设最好的情况,每次去操作数据都认为不会被别的线程修改数据,所以在每次操作数据的时候都不会给数据加锁,即在线程对数据进行操作的时候,别的线程不会阻塞仍然可以对数据进行操作,只有在需要更新数据的时候才会去判断数据是否被别的线程修改过,如果数据被修改过则会拒绝操作并且返回错误信息给用户。
  2. 悲观锁 悲观锁总是假设最坏的情况,每次去操作数据时候都认为会被的线程修改数据,所以在每次操作数据的时候都会给数据加锁,让别的线程无法操作这个数据,别的线程会一直阻塞直到获取到这个数据的锁。这样的话就会影响效率,比如当有个线程发生一个很耗时的操作的时候,别的线程只是想获取这个数据的值而已都要等待很久。

Java利用CAS的乐观锁、原子性的特性高效解决了多线程的安全性问题,例如JDK1.8中的集合类ConcurrentHashMap、关键字volatileReentrantLock等。

参考地址

如果大家喜欢我的文章,可以关注个人订阅号。欢迎随时留言、交流。如果想加入微信群的话一起讨论的话,请加管理员简栈文化-小助手(lastpass4u),他会拉你们进群。

简栈文化服务订阅号

本文转载自:http://www.cyblogs.com/casyuan-li-fen-xi-ji-abawen-ti-xiang-jie/

Onegoleya
粉丝 56
博文 107
码字总数 58646
作品 0
深圳
技术主管
私信 提问
加载中

评论(0)

Java并发编程-无锁CAS与Unsafe类及其并发包Atomic

出自【zejian的博客】 在前面一篇博文中,我们曾经详谈过有锁并发的典型代表synchronized关键字,通过该关键字可以控制并发执行过程中有且只有一个线程可以访问共享资源,其原理是通过当前线...

芝麻绿豆
2017/08/07
681
0
乐观锁的一种实现方式——CAS

在深入理解乐观锁与悲观锁一文中我们介绍过锁。本文在这篇文章的基础上,深入分析一下乐观锁的实现机制,介绍什么是CAS、CAS的应用以及CAS存在的问题等。 线程安全 众所周知,Java是多线程的...

vshcxl
2016/12/19
42
0
(java并发)CAS操作原理以及Atomic的原理

CAS操作可以分为以下三个步骤: 步骤 1.读旧值(即从系统内存中读取所要使用的变量的值,例如:读取变量i的值) 步骤2.求新值(即对从内存中读取的值进行操作,但是操作后不修改内存中变量的值...

vshcxl
2016/11/28
254
0
ConcurrentLinkedQueue cas实现分析

淘宝清英在此文对ConcurrentLinkedQueue做了详细分析 http://www.infoq.com/cn/articles/ConcurrentLinkedQueue cas实现没有涉及,我又读了一下ConcurrentLinkedQueue.Node类,记录此文作为补...

yingtju
2018/06/29
0
0
并发编程(二)——JAVA CAS原理深度分析

看了一堆文章,终于把JAVA CAS的原理深入分析清楚了。 感谢GOOGLE强大的搜索,借此挖苦下百度,依靠百度什么都学习不到! 参考文档: http://www.blogjava.net/xylz/archive/2010/07/04/3252...

whc20011
2016/10/27
37
0

没有更多内容

加载失败,请刷新页面

加载更多

如何使div的浏览器窗口高度为100% - How to make a div 100% height of the browser window

问题: I have a layout with two columns - a left div and a right div . 我有两列的布局-左div和右div 。 The right div has a grey background-color , and I need it to expand vertic......

技术盛宴
今天
20
0
SSM框架整合

mybatis逆向工程 mybatis-generator生成pojo、mapper接口及映射文件 mapper放到e3-manager-dao层中 导入sql到数据库中; 导入逆向工程工具,配置xml文件 运行main方法 重复运行main不会覆盖!...

七宝1
今天
30
0
OSChina 周日乱弹 —— 和网友的第一次开房经历

Osc乱弹歌单(2020)请戳(这里) 【今日歌曲】 @薛定谔的兄弟 :分享洛神有语创建的歌单「我喜欢的音乐」: 《Ljósið》- Ólafur Arnalds 手机党少年们想听歌,请使劲儿戳(这里) @xiaos...

小小编辑
今天
50
0
程序员职业生涯指引

程序员应该尽早规划自己的职业生涯 为什么写 众所周知 IT 这一行到了一定的年龄、大部分人都或多或少有危机感,特别是今年全国乃至全球发生的疫情、导致整体经济受到很大的影响、这次的疫情影...

科比可比克
今天
11
0
JVM调优实战分析

一、查看服务器项目JVM参数以及参数分析 1、jps 命令 : 列出系统中所有的 Java 应用程序以及PID 如下图所示,26647就是我部署在服务器的一个小项目的 PID 2、jmap命令:查看堆的使用情况 如...

IT-Mamba
今天
46
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部