java多线程之CAS

来源:互联网 发布:计算机美工 编辑:程序博客网 时间:2024/06/04 20:31

CAS操作
在java中可以通过CAS操作来实现操作的原子性。cas即compare and set的缩写。JVM中的CAS操作利用了处理器提供的CMPXCHG指令实现。自旋CAS实现的基本思路就是循环进行CAS操作直到成功为止。下面的代码展示的是如何通过CAS算法来实现线程同步:

package chapter02;import java.util.ArrayList;import java.util.List;import java.util.concurrent.atomic.AtomicInteger;public class CAS {    //使用java concurrent包中的AtomicInteger类来实现CAS操作    private AtomicInteger atomic = new AtomicInteger(0);    //非线程安全的变量i    private int i = 0;    public static void main(String args[]) {        final CAS cas = new CAS();        List<Thread> ts = new ArrayList<Thread>(600);        long start = System.currentTimeMillis();        for (int i = 0; i < 100; i++) {            Thread t = new Thread(new Runnable() {                @Override                public void run() {                    for(int j=0;j<10000;j++){                        cas.count();                        cas.safeCount();                    }                }            });            ts.add(t);        }        for(Thread t :ts){            t.start();        }        for(Thread t:ts){            try{                t.join();            }catch(Exception e){                e.printStackTrace();            }        }        System.out.println("unsafe"+cas.i);        System.out.println("safe"+cas.atomic.get());        System.out.println(System.currentTimeMillis()-start);    }    //线程安全的Count方法    private void safeCount() {        for (;;) {            //atomic的get()方法获取volatile变量value            int i = atomic.get();            //如果设置成功,则获得运行权,否则所有的线程都通过自旋的方法获取执行权            boolean suc = atomic.compareAndSet(i, ++i);            if (suc) {                break;            }        }    }    //非线程安全的count方法    private void count() {        i++;    }}

上面的代码运行结构为:
unsafe834081
safe1000000
87

CAS原理:
通过查看AtomicInteger的源码可知,
`private volatile int value;

public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}`
通过申明一个volatile (内存锁定,同一时刻只有一个线程可以修改内存值)类型的变量,再加上unsafe.compareAndSwapInt的方法,来保证实现线程同步的。

缺点:
1、ABA问题
CAS操作容易导致ABA问题,也就是在做a++之间,a可能被多个线程修改过了,只不过回到了最初的值,这时CAS会认为a的值没有变。a在外面逛了一圈回来,你能保证它没有做任何坏事,不能!!也许它讨闲,把b的值减了一下,把c的值加了一下等等,更有甚者如果a是一个对象,这个对象有可能是新创建出来的,a是一个引用呢情况又如何,所以这里面还是存在着很多问题的,解决ABA问题的方法有很多,可以考虑增加一个修改计数,只有修改计数不变的且a值不变的情况下才做a++,也可以考虑引入版本号,当版本号相同时才做a++操作等,这和事务原子性处理有点类似!
2、比较花费CPU资源,即使没有任何争用也会做一些无用功。
3、会增加程序测试的复杂度,稍不注意就会出现问题。

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

1 0
原创粉丝点击