(java并发)CAS操做原理以及Atomic的原理

CAS操做能够分为如下三个步骤:java

   步骤 1.读旧值(即从系统内存中读取所要使用的变量的值,例如:读取变量i的值)算法

   步骤2.求新值(即对从内存中读取的值进行操做,可是操做后不修改内存中变量的值,例如:i=i+1,这一步只进行                   i+1,没有赋值,不对内存中的i进行修改)多线程

   步骤3.两个不可分割的原子操做并发

              第一步:比较内存中变量如今的值与 最开始读的旧值是否相同(即从内存中从新读取i的值,与一开始读取的                              i进行比较)框架

              第二步:若是这两个值相同的话,则将求得的新值写入内存中(即:i=i+1,更改内存中的i的值)测试

                             若是这两个值不相同的话,则重复步骤1开始this

             注:这两个操做是不可分割的原子操做,必须两个同时完成atom

 

 

 

CAS操做

 

CAS是单词compare and set的缩写,意思是指在set以前先比较该值有没有变化,只有在没变的状况下才对其赋值。spa

咱们经常作这样的操做.net

 

if(a==b) {  
    a++;  
}

试想一下若是在作a++以前a的值被改变了怎么办?a++还执行吗?出现该问题的缘由是在多线程环境下,a的值处于一种不定的状态。采用锁能够解决此类问题,但CAS也能够解决,并且能够不加锁。

int expect = a;  
if(a.compareAndSet(expect,a+1)) {  
    doSomeThing1();  
} else {  
    doSomeThing2();  
}

 

这样若是a的值被改变了a++就不会被执行。

按照上面的写法,a!=expect以后,a++就不会被执行,若是咱们仍是想执行a++操做怎么办,不要紧,能够采用while循环

 

while(true) {  
    int expect = a;  
    if (a.compareAndSet(expect, a + 1)) {  
        doSomeThing1();  
        return;  
    } else {  
        doSomeThing2();  
    }  
}  

采用上面的写法,在没有锁的状况下实现了a++操做,这其实是一种非阻塞算法

 

应用

Java.util.concurrent.atomic包中几乎大部分类都采用了CAS操做,以AtomicInteger为例,看看它几个主要方法的实现:

 

public final int getAndSet(int newValue) {  
    for (;;) {  
        int current = get();  
        if (compareAndSet(current, newValue))  
            return current;  
    }  
}  

getAndSet方法JDK文档中的解释是:以原子方式设置为给定值,并返回旧值。原子方式体如今何处,就体如今compareAndSet上,看看compareAndSet是如何实现的:

public final boolean compareAndSet(int expect, int update) {  
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);  
}  

不出所料,它就是采用的Unsafe类的CAS操做完成的。

 

 

 

再来看看a++操做是如何实现的:

public final int getAndIncrement() {  
    for (;;) {  
        int current = get();  
        int next = current + 1;  
        if (compareAndSet(current, next))  
            return current;  
    }  
}  

几乎和最开始的实例如出一辙,也是采用CAS操做来实现自增操做的。

 

++a操做和a++操做相似,只不过返回结果不一样罢了

public final int incrementAndGet() {  
    for (;;) {  
        int current = get();  
        int next = current + 1;  
        if (compareAndSet(current, next))  
            return next;  
    }  
}  

此外,java.util.concurrent.ConcurrentLinkedQueue类全是采用的非阻塞算法,里面没有使用任何锁,全是基于CAS操做实现的。CAS操做能够说是JAVA并发框架的基础,整个框架的设计都是基于CAS操做的。

 

缺点:

一、ABA问题

CAS操做容易致使ABA问题,也就是在作a++之间,a可能被多个线程修改过了,只不过回到了最初的值,这时CAS会认为a的值没有变。a在外面逛了一圈回来,你能保证它没有作任何坏事,不能!!也许它讨闲,把b的值减了一下,把c的值加了一下等等,更有甚者若是a是一个对象,这个对象有多是新建立出来的,a是一个引用呢状况又如何,因此这里面仍是存在着不少问题的,解决ABA问题的方法有不少,能够考虑增长一个修改计数,只有修改计数不变的且a值不变的状况下才作a++,也能够考虑引入版本号,当版本号相同时才作a++操做等,这和事务原子性处理有点相似!

二、比较花费CPU资源,即便没有任何争用也会作一些无用功。

三、会增长程序测试的复杂度,稍不注意就会出现问题。

 

总结:

能够用CAS在无锁的状况下实现原子操做,但要明确应用场合,很是简单的操做且又不想引入锁能够考虑使用CAS操做,当想要非阻塞地完成某一操做也能够考虑CAS。不推荐在复杂操做中引入CAS,会使程序可读性变差,且难以测试,同时会出现ABA问题。

相关文章
相关标签/搜索