java并发之CAS

作者: miaoLoveCode | 来源:发表于2018-06-21 21:15 被阅读509次

写在最前面

在上文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用于更新对象。

相关文章

  • JAVA并发编程学习笔记之CAS操作

    JAVA并发编程学习笔记之CAS操作 CAS操作 CAS是单词compare and set的缩写,意思是指在se...

  • Java并发之原子变量及CAS算法-下篇

    Java并发之原子变量及CAS算法-下篇 概述 本文主要讲在Java并发编程的时候,如果保证变量的原子性,在JDK...

  • Java并发之原子变量及CAS算法-上篇

    Java并发之原子变量及CAS算法-上篇 概述 本文主要讲在Java并发编程的时候,如果保证变量的原子性,在JDK...

  • 并发的核心:CAS 是什么?Java8是如何优化 CAS 的?

    并发的核心:CAS 是什么?Java8是如何优化 CAS 的? 大家可能都听说说 Java 中的并发包,如果想要读...

  • java并发之CAS

    写在最前面 在上文java并发之volatile末尾有提到,volatile并不能保证++操作的线程安全。我们来通...

  • Java并发之CAS

    一、什么是CAS   CAS(Compare And Swap,比较和交换),通常指的是这样一种原子操作:在修改某...

  • Java CAS

    CAS和LockSupport可以说贯穿了java并发包(自旋锁 + CAS + LockSupport + 内存...

  • Java并发系列 — CAS

    原文地址:【死磕Java并发】—-深入分析CAS CAS,Compare And Swap,即比较并交换。Doug...

  • 技术栈

    技术栈 Java Web 并发基础 [ ] CopyOnWrite [ ] 线程池 [ ] CAS [ ] AQS...

  • 并发编程-CAS

    Java并发编程中,除了通过synchronized进行并发控制外,还可以通过CAS(Compare And Se...

网友评论

本文标题:java并发之CAS

本文链接:https://www.haomeiwen.com/subject/ooqcyftx.html