java并发之CAS

写在最前面

在上文java并发之volatile末尾有提到,volatile并不能保证++操作的线程安全。我们来通过一个简单的例子看下为什么。

++测试demo

通过javap -v看下其反编译后字节码指令:
反编译结果

从反编译结果可以看出,++被拆成了这样三个指令:

  1. getfield:获取变量count中的值;

  2. iadd:进行+1操作;

  3. putfield:将+1后的数据写回count。

volatile虽然可以保证变量的可见性,但是并不能保证这三个操作的原子性,所以它不能保证++操作的线程安全,那++操作的线程安全要怎么解决呢?

注:何谓原子性?
原子性是指一个操作不能被中断,即使是在多个线程一起执行的时候,一旦开始,也不会被其他线程打断。

  1. 第一种简单粗暴的方法就是加锁了,但是这样做开销有点大了耶;

  2. 第二种方法就是使用JDK的CAS了,juc包下也有相关Atomic类可以支持,使用简单,而且高效。(哈哈哈,终于绕回了本文的主题啊,不容易啊~~~)

接下来我们就通过分析CAS相关源码实现来看看CAS是如何做到线程安全的。

CAS原理

CAS,Compare and swap,比较和替换。CAS的操作包括三个参数:内存位置(V)、预期原值(A)和新的值(B),如果V和A相同,处理器会将该位置的值修改为B,否则,处理器不会做任何操作。

在前文java并发编程之原子类已经详细介绍了JDK提供的Atomic相关原子类的使用及实现,它们的实现都很神似,都是通过Unsafe提供的compareAndSwap开头的方法来完成对value的并发修改的。接下来我们就以compareAndSwapInt方法为例来看看它到底是怎么保证并发修改的安全性。

compareAndSwapInt实现

compareAndSwapInt声明

从声明可以看出,compareAndSwapInt是一个native的方法,该方法的实现位于unsafe.cpp中:

compareAndSwapInt实现.png

从源码可以看出,compareAndSwapInt主要做了这些事情:

  1. 获取value在内存中的地址;

  2. 调用Atomic::cmpxchg方法完成比较替换,其中e是原始值,x是新的值。

接下来我们来看看Atomic::cmpxchg的相关实现:

注:以linux x86平台为例,具体实现在atomic_linux_x86.inline.hpp中

Atomic::cmpxchg实现

在开始介绍源码之前先科普下这里用到的内嵌汇编的关键字:

  1. __asm__:用来声明一个内联汇编表达式,任何一个内联汇编表达式都是以它开头的,是必不可少的;

  2. __volatile__或者volatile__volatile__是GCC关键字volatile的宏定义,如果用了它,则是向GCC声明不允许对该内联汇编优化,否则当使用了优化选项(-O)进行编译时,GCC将会根据自己的判断决定是否将这个内联汇编表达式中的指令优化掉。

有了这些科普我们再来看代码是不是就和谐多了,Atomic::cmpxchg其实就做了这样几件事:

  • 判断当前系统是否是多核处理器;

  • 依赖指令cmpxchg完成比较替换,如果当前系统是多核处理器,将会在指令前加lock前缀,否则不加。其中具体是否要加lock前缀的处理在宏LOCK_IF_MP中定义:

    LICK_IF_MP声明

lock前缀保证了CAS并发操作的安全性。

注:在上文java并发之volatile中已经详细介绍过lock前缀的意义,在这里我就不再赘述了,有需要了解的小伙伴请自行移步啦~

CAS存在的问题

CAS虽然可以很高效原子操作,但是它也是存在问题的。

  1. ABA问题
    因为CAS会在更新值的时候会检查值有没有发生改变,如果没有发生改变就更新,否则不更新,但是如果一个值原来是A,变成了B,再变回A,这时候CAS进行检查时会误认为它没有发生变化。针对这种情况,juc包提供了AtomicStampedReference,通过控制变量值的版本号来解决ABA问题。

  2. 循环时间长开销大
    自旋CAS如果长时间不成功会给CPU带来比较大的执行开销。

  3. 只能保证一个共享变量的原子操作
    对一个共享变量的操作,使用循环CAS肯定可以保证其原子性,但是如果对多个变量操作时,CAS就比较鸡肋了。当然有一个办法就是把几个共享变量合并成一个大的对象,然后CAS操作这个大对象。同样,juc包也提供AtomicReference用于更新对象。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,039评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,223评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,916评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,009评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,030评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,011评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,934评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,754评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,202评论 1 309
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,433评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,590评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,321评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,917评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,568评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,738评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,583评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,482评论 2 352

推荐阅读更多精彩内容

  • 高山流水,珠落玉盘。水带麻山春,杯含清净影。 一粒粟中藏世界,半升铛中煮山河。 寂寂麻山墓,凭吊几多人。潇潇雨,斜...
    jingtu阅读 422评论 0 0