多个线程并发执行的时候,volatile并不能保证原子性的,所以在一个线程希望对资源加1时,另一个线程可能也想做同样的操作。那么如何避免这种操作带来的问题。

alt

cas的核心思想:(比较期望值是否和原值相同,如果不同则宣告失败从而不去赋值)

1.通过补偿重试的自旋方式实现这种操作的,好处是不用加锁,并且不用排队、;

2.cas(compare and swap)比较并交换的方式,这种方式是基于cpu能否支持的方式来实现的;模拟cpu的比较并交换来实现的;

alt

3.伪代码

alt


public class CASTest {
    //CAS的核心思想是取值,比较,赋值的三个步骤,在赋值之前如果期望值和实际的值相同才会将更新后的值刷回主存中,如果不相同那么就采用自旋的方式
    //不断地尝试补偿。
    public static volatile int COUNT;
    public synchronized static boolean compareAndSwap(int except, int update){//其实这样做并不是使用了cas字段实现的;
        if (except == COUNT){//期望值和实际的值相等才会交换,并且返回true否则返回false;
            COUNT = update;
            return true;
        }
        return false;
    }
    public static void main(String[] args) {
        for (int i = 0; i < 500; i++) {
            new Thread(()->{
                boolean flag = false;
                while (!flag){
                    flag = compareAndSwap(COUNT,COUNT + 1);//
                }
            }).start();
        }

        ThreadUtil.sleep(3000);
        System.out.println(COUNT);
    }
    //最会的结果并不一定是500
}

cas的作用:解决了对变量修改的原子性问题,这个问题是volatile解决不掉的问题;

cas的缺陷:普通的cas操作可能存在ABA问题,比如对于引用数据类型,线程2已经将取到的引用对象指向的一个对象修改成了其他的值,让偶将这个对象刷回主存中,但是线程1同样的对取到了引用,但是判断之后发现一样,于是通过了刷入。但实际上已经出现了不同,java对cas产生的ABA问题提供了一个类叫做AtomicStampeReference来解决这个问题,一旦被修改之后就将版本号加1,以确保前后的对象一致。

ABA问题的产生一般不影响,如果有一天需要解决ABA问题,那么说明水平很不错了

一句话:使用volatile配合cas能够实现线程安全的,操作一个变量的一个类,即原子性操作。和synchronized实现的方式相同;但是区别在于synchronized是重量级锁,过于复杂了。volatile+cas就没有锁的概念,所以在线程的竞争不大时能够节省很多的开销。