CAS算法

2019-08-22  本文已影响0人  hcq0514

CAS 是什么

CAS实现例子,以AtomicInteger为例

// AtomicInteger初始化过程
AtomicInteger atomicInteger = new AtomicInteger(666);

public AtomicInteger(int initialValue) {
        value = initialValue;
}

private volatile int value;
// AtomicInteger执行getAndIncrement();
    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1); 
       //this:当前对象,valueOffset:value的内存地址
    }

    public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5; //期望值
        do {
            var5 = this.getIntVolatile(var1, var2); 
//获取主内存中的value值(因为value为volatile,所以变化是实时可见的)
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
// compareAndSwapInt里会重新再取一次主内存的值,跟var5我们传入的期望值相比较,
// 如果此时期望值跟我们主内存的值相等时(也就是值没有被修改过)才会退出循环,否则重新获取

        return var5;
    }

CAS 的缺点?

ABA 问题(原子类 AtomicInteger 的 ABA 问题谈一谈?原子更新引用知道吗?)

        User user = new User().setName("hcq").setAge(24);
        AtomicReference<User> atomicReference = new AtomicReference<>();
        //首先开一个线程来把这个值修改两次
        threadPool.submit(() -> {
            boolean firstCAS = atomicReference.compareAndSet(null, user);
            System.out.println(Thread.currentThread().getName() + ",第一次修改" + firstCAS + ",变量数据" + atomicReference.get());
            boolean secondCAS = atomicReference.compareAndSet(user, null);
            System.out.println(Thread.currentThread().getName() +",第二次修改" + secondCAS + ",变量数据" + atomicReference.get());

        });
        //休眠2秒,保证前面的执行完成
        Thread.sleep(2000);
        boolean threeCAS = atomicReference.compareAndSet(null, user);
        System.out.println(Thread.currentThread().getName() +",第三次修改" + threeCAS + ",变量数据" + atomicReference.get());
pool-1-thread-1,第一次修改true,变量数据User(name=hcq, age=24)
pool-1-thread-1,第二次修改true,变量数据null
main,第三次修改true,变量数据User(name=hcq, age=24)

由输出结果可见,thread-1已经将atomicReference改了两次,但是对main线程来说,他最后的值还是null,对于他来说是没有任何改变的。

AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1, 1);
//这个类的CAS方法是带时间戳判定的。
 public boolean compareAndSet(V   expectedReference,
                                 V   newReference,
                                 int expectedStamp,
                                 int newStamp) {
        Pair<V> current = pair;
        return
            expectedReference == current.reference &&
            expectedStamp == current.stamp &&
            ((newReference == current.reference &&
              newStamp == current.stamp) ||
             casPair(current, Pair.of(newReference, newStamp)));
    }
上一篇 下一篇

猜你喜欢

热点阅读