JAVA并发编程(三)-CAS和原子变量

Scroll Down

JAVA并发编程(三)-CAS和原子变量

CAS介绍

CAS(compare and swap)是对一种处理器指令的称呼,java中很多类的实现会借助CAS.CAS可以将read-modify-write和check-and-act操作转换为原子操作,类似于count++这种操作,使用CAS转换为if-then-act操作,用伪代码来描述可以这样描述

  public boolean CAS(Variable V,Object A,Object B)
  if(A == V.getValue()){
  	V.set(B);
  	return true;
  }
  return false;

CAS是一个原子的if-then-act操作,假设一个线程执行CAS操作的时候,变量V的当前值和客户请求CAS提供的变量值是相等的,那么就说明其他线程没有修改过变量V的值,那么当前线程就会抢先将变量V的值修改为B值,其他线程的更新请求将会失败,失败的线程可以选择在此尝试,直到成功.

public class CASBasedCounter {
    /**
     * 这里使用AtomicLongFieldUpdater只是为了便于讲解和运行该实例,
     * 实际上更多的情况是我们不使用AtomicLongFieldUpdater,而是使用
     * java.util.concurrent.atomic包下的其他更为直接的类。
     */
    private final AtomicLongFieldUpdater<CASBasedCounter> fieldUpdater;
    private volatile long count;

    public CASBasedCounter() throws SecurityException, NoSuchFieldException {
        fieldUpdater = AtomicLongFieldUpdater.newUpdater(CASBasedCounter.class,
                "count");
    }

    public static void main(String[] args) throws Exception {
        final CASBasedCounter counter = new CASBasedCounter();
        Thread t;
        Set<Thread> threads = new HashSet<Thread>();
        for (int i = 0; i < 20; i++) {
            t = new Thread(new Runnable() {
                @Override
                public void run() {
                    Tools.randomPause(50);
                    counter.increment();
                }
            });
            threads.add(t);
        }

        for (int i = 0; i < 8; i++) {
            t = new Thread(new Runnable() {
                @Override
                public void run() {
                    Tools.randomPause(50);
                    Debug.info(String.valueOf(counter.vaule()));
                }
            });
            threads.add(t);
        }

        // 启动并等待指定的线程结束
        Tools.startAndWaitTerminated(threads);
        Debug.info("final count:" + String.valueOf(counter.vaule()));
    }

    public long vaule() {
        return count;
    }

    public void increment() {
        long oldValue;
        long newValue;
        do {
            oldValue = count;// 读取共享变量当前值
            newValue = oldValue + 1;// 计算共享变量的新值
        } while (/* 调用CAS来更新共享变量的值 */!compareAndSwap(oldValue, newValue));
    }

    /*
     * 该方法是个实例方法,且共享变量count是当前类的实例变量,因此这里我们没有必要在方法参数中声明一个表示共享变量的参数
     */
    private boolean compareAndSwap(long oldValue, long newValue) {
        boolean isOK = fieldUpdater.compareAndSet(this, oldValue, newValue);
        return isOK;
    }

CAS只能保证变量更新操作的原子性,不保证可见性,还是需要volatile来保证可见性.

CAS也有可能会产生这种场景,原始值是A,线程T1将值改成B,线程T2将值改成A,此时线程T3获取到的值是第一次的A,但是他还是认为是最新值,可以成功去更新。这就是A->B->A问题,有些业务场景下是不可接受这种问题的,如果这样可以加上一个版本号或者时间戳,每个值更新时将版本号加1,根据值+版本号来确定是否是最新值。AtomicStampedReference 类就是根据这种思想产生的类。

原子变量类

分组类名
基础数据型AtomicInteger、AtomicLong、AtomicBoolean
数组型AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
字段更新器AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater
引用型AtomicReference、AtomicStampedReference、AtomicMarkableReference

例如AtomicInteger继承与Number类,里面使用了volatile修饰了value变量,使用get()方法相当于读取一个volatile变量,而使用incrementAndGet()方法相当于自增,底层还是通过CAS方式来实现的

 public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
  }
 public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }