线程基础知识02-CAS (CompareAndSwap)

1.基本概念  

  原子性是不可中断的最小操作;在Java中,一般通过加锁或者自旋CAS方式来确保原子操作;

  而CAS(compareAnd swap)作为Java中常用的保证原子性的手段,JDK1.5之后就提供了相关的操作类,java.util.concurrent.atomic包中的工具类;

  CAS概念:CAS操作需要两个值,旧值(预期的操作之前的值)和新值,先将旧值进行比较,如果旧值没有发生变化,则进行操作,如果旧值变化则不进行任何操作

 2.常用来解释CAS的一个案例:

public class CASTest {
    private int i = 0;
    private AtomicInteger ai = new AtomicInteger();
    private static final Integer NUM = 10;
    private static CountDownLatch latch = new CountDownLatch(NUM);

    public static void main(String[] args) throws InterruptedException {
        final CASTest t = new CASTest();
        for(int i =0 ; i < NUM ; i++){
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    for(int j = 0 ; j < 100 ; j++){
                        t.count();
//                        t.safeCount();
                    }
                    latch.countDown();
                }
            });
            thread.start();
        }
        latch.await();
        System.out.println("======= over ======="+t.i);
    }

    public  void count(){
        i ++ ;
    }
    
    public void safeCount(){
        for(;;){
            int i = ai.get();
            boolean b = ai.compareAndSet(i, i++);
            if( b ){
                break ;
            }
        }

    }
}

理想的结果是1000,但是非CAS的结果总是会有差异,而safeCount 采用的是CAS的方式进行原子操作;

3.CAS 操作存在的问题和解决方案

1.ABA问题

  问题描述:首先,假设一个变量经过A=>B=>A的变化过程,一个线程进行,获取到操作前的值为A,按照上面的概念这个线程获取到的旧值A,和这个变量进行变化后的现在的新值A是一样的。如果进行CAS自旋操作无疑是有问题的。

  解决方案:JDK中atomic包中提供了,AtomicStampedReference 类,提供了更完善的方法。通过增加标志,来对旧值进行判断。

 

 2.循环时间长开销大

  问题描述:当一个线程修改的值,被其他线程再次修改后,自旋旧值比对长时间无法获取成功,导致资源的浪费;

       解决方案:JVM中提供了puse指令可以解决这种问题;

3.只能保证一个变量的原子操作

  问题描述:如题

       解决方案:通过将多个变量封装成一个对象,通过atomic包中的AtomicReference类来进行解决;

 

 

参考图书《java并发编程艺术》

最后,

 

posted @ 2020-04-23 16:10  PerfectLi  阅读(190)  评论(0编辑  收藏  举报