java多线程-cas及atomic

大纲:

  1. cas
  2. atomic

一、cas

cas:compareAndSwap,一种乐观锁。

cas思想:cas需要三个值,v是内存值,e是期望值,n是要修改的值。当内存中的值v等于预期值e(说明内存中的值没有被其他线程修改)、才可以修改v为n。

cas伪代码

boolean cas(int v,int e,int n){
if(v==e){
v=n;
return true;
}else {
return false;
}
}

cas是unsafe类中的native方法,是原子操作,保证线程安全。

二、atomic

在java.util.concurrent.atomic包中,提供了许多拥有原子操作的类,供并发编程使用。这个包中的类大都使用了cas操作来保证操作的原子性。

以AtomicInteger为例,看下如何使用cas实现原子操作,这里为java1.8以下的源码:

public class AtomicInteger extends Number implements java.io.Serializable {

    private volatile int value; //volatile保证可见性,一个线程更新后,其他线程立即可见

    public final int get() {
        return value;
    }

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

    public final int getAndIncrement() {
        for (;;) {
            int current = get();//预期值e
            int next = current + 1;//要修改的值n
            if (compareAndSet(current, next))
                return current;
        }
        //这个相当于线程安全的a++操作,其思想就是不断进行cas操作,直至成功
    }

    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
        //给bootStrap类加载器用的unsafe中全部是native方法。
        // valueOffset为变量在内存中偏移地址,用来找内存中的值v
    }

    ......
}

 ABA问题:一个线程在cas操作时,期望值是A,另一个线程修改了期望值为B,就会导致cas操作失败。但是如果这时候又有一个线程再次修改期望值为A,cas操作将成功。

如何解决:atomic包下AtomicStampedReference

public class AtomicStampedReference<V> {

    private static class Pair<T> {
        final T reference;
        final int stamp;
        private Pair(T reference, int stamp) {
            this.reference = reference;
            this.stamp = stamp;
        }
        static <T> Pair<T> of(T reference, int stamp) {
            return new Pair<T>(reference, stamp);
        }
    }

    private volatile Pair<V> pair;

    public AtomicStampedReference(V initialRef, int initialStamp) {
        pair = Pair.of(initialRef, initialStamp);
        //AtomicStampedReference内部有一个静态内部类pair,pair不仅封装了数据还封装了一个stamp用于区分每次修改
    }

    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))); //cas比较pair对象(不仅比较内存值如期望值,同时比较期望stamp与内存stamp)
    }

    private boolean casPair(Pair<V> cmp, Pair<V> val) {
        return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
    }

    ...
}
原文地址:https://www.cnblogs.com/liuboyuan/p/10449503.html