java多线程之CAS操作AtomicXXX原子操作类,ABA问题以及原子更新字段类详解

原子操作是什么

原子操作即不可被中断(分割)的一个或一系列操作,如对于A,B两个操作,如果一个线程操作A,若另一个线程执行B操作时,要么将B执行到结束,要么完全不让B执行,此时对于A和B来说,就是原子的。

原子操作不存在上下文切换。
与数据库的原子性比,数据库支持回滚,而这里的原子不存在回滚。

悲观锁和乐观锁

synchronized就是悲观锁,解决多线程并发问题,以保证事务的完整性。其是基于阻塞的锁机制,如果有一个线程拥有锁后,访问该资源的其他线程就需要阻塞等待,直到获得锁的线程释放锁。
CAS操作即是乐观锁,每个线程都可以访问,只有在提交数据的时候检查是否违反了数据的完整性,即每次都尝试去完成某个操作,如果冲突和造成操作失败,就循环重试,直到操作成功为止。

使用synchronized会引出很多问题,如:获得锁的线程一直不释放锁;大量线程竞争资源,可能会造成死锁;被阻塞的线程可能优先级很高却一直无法获取锁。

因此实现原子操作可使用CAS指令。

CAS

CAS 即Compare and Swap,比较并交换。每个CAS操作过程都包括三个运算符:
内存地址V、期望值A、新的值B
如果操作的时候地址V上的值等于期望的值A,则将地址V上的值更新为B,否则不做任何操作,但要返回原值。循环CAS就是不断的进行CAS操作直到操作成功。可防止内存中共享变量出现脏读脏写的问题。
CAS是通过硬件CPU和内存,利用CPU的多处理能力实现硬件层面的阻塞,再加上volatile变量的特性来实现基于原子操作的线程安全。

(1)get变量值(旧值)----->
(2)计算后得到新值------>
(3)比较内存中变量值和旧值----->
(4)如果(3)相等,则让新值替换旧值,否则继续(1)

CAS实现原子操作的三大问题

ABA问题

由于CAS在操作值的时候需要检查旧值是否发生变化,如果未变化则更新值,但如果一个值原来是A,变成了B,然后又变为了A,这样虽然值已经发生了变化,但使用CAS进行检查时会发现它的旧值“未变化”。
而要解决该问题,就是使用版本号。即给变量添加版本号,每次更新变量都去更新它的版本号,如刚刚A->B->A,便变成了 1A->2B->3A。就像在家里倒了一杯水,你还没来得及喝,但是被家人喝掉, 然后又给你接满了一杯水,这样如果在不知道的情况下,你会认为这仍然是之前那杯水。又或者你的资金被别人挪用了,然后又还给了你,虽然钱没有变,但造成的问题时那个人已经犯罪了。

解决ABA问题:
使用版本戳

线程A读取了数据X=1,版本为1,线程B修改了x=5,更新版本为2,线程C修改X=1,更新版本为3,此时线程A再修改X,发现虽然值一样,但版本已经不一样了。这样就解决的ABA问题。

循环时间长,开销大

如果CAS一直处于自旋不成功状态,CPU的开销会大大增加。

只能保证一个共享变量的原子操作

当对一个共享变量执行操作时,可使用循环 CAS 的方式来保证原子操作,但是对多个共享变量操作时,循环 CAS 就无法保证操作的原子性,当然可以用锁。
也可以把多个共享变量合并成一个共享变量来操作。如,有两个共享变量 i=a,j=b,合并一下 ij=ab,然后用 CAS 来操作 ij,然后通过AtomicReference 类来保证引用对象之间的原子性,便实现了多个变量放在一个对象里来进行 CAS 操作。

JDK中相关原子操作类的使用

概览

更新基本类型类:AtomicBooleanAtomicIntegerAtomicLong
更新数组类:AtomicIntegerArrayAtomicLongArrayAtomicReferenceArray
更新引用类型AtomicReferenceAtomicMarkableReferenceAtomicStampedReference
原子更新字段类: AtomicReferenceFieldUpdaterAtomicIntegerFieldUpdaterAtomicLongFieldUpdater

基本数据类型的原子操作类以AtomicInteger为例,其余差不多。

AtomicInteger

属性
private static final long serialVersionUID = 6214790243416807050L;
 
    //获取指针类Unsafe
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    //内存偏移量
    private static final long valueOffset;
 
    static {
        try {
           //通过objectFieldOffset()方法,获取value字段的偏移量
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }
   	//当前的变量值
    private volatile int value;

使用int类型的value保存值,使用volatile修饰,保证了可见性和有序性
常用方法如下:

public class AtomicInteger extends Number implements java.io.Serializable {
    
 
    public AtomicInteger(int initialValue) {
    	//初始化变量值
        value = initialValue;
    }
    //value初始化为0
    public AtomicInteger() {
    }
   	//返回当前值
    public final int get() {
        return value;
    }
    //设置当前值
    public final void set(int newValue) {
        value = newValue;
    }
    //懒设置,使用Unsafe.putOrderedObject方法实现,实现非堵塞的写入,最终会将value置为newValue。
    //该方法可能出现其他线程在未来很小的一段时间内无法获取到新值,但可获取到旧值
    //因为putOrderedObject是保证了有序性,不保证可见性。
    public final void lazySet(int newValue) {
        unsafe.putOrderedInt(this, valueOffset, newValue);
    }
   //以原子方式设置为给定值并返回旧值。
    public final int getAndSet(int newValue) {
        return unsafe.getAndSetInt(this, valueOffset, newValue);
    }
    //以下是getAndSetInt源码,通过while循环重试更新值,直到成功,也是通过CAS实现(JDK8版本)
     /*public final int getAndSetInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var4));

        return var5;
    }*/
   //原子更新value,如果当前值等于expect,则设置为update
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
    //以原子方式将当前值增加一
    //返回旧值,底层同getAndSetInt一样使用CAS操作
    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }
    //以原子方式将当前值减一
    //返回旧值,CAS操作
    public final int getAndDecrement() {
        return unsafe.getAndAddInt(this, valueOffset, -1);
    }
   //以原子方式将给定值delta添加到当前值
   //返回旧值,CAS操作
    public final int getAndAdd(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta);
    }
    //以原子方式将当前值加1,返回新值
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }
    //当前值减1,返回新值
    public final int decrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
    }
   //以原子方式将当前值增加delta,返回新值
    public final int addAndGet(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
    }
    //使用给定函数的结果以原子方式更新当前值,返回更新后的值。给的函数是无副作用的,因为当尝试更新由于线程之间的争用而失败时,它可能会重新应用
     public final int updateAndGet(IntUnaryOperator updateFunction) {
        int prev, next;
        do {
            prev = get();
            next = updateFunction.applyAsInt(prev);
        } while (!compareAndSet(prev, next));
        return next;
    }
    //同上,使用给定函数的结果以原子方式更新当前值,返回更新前的值。应用该函数时,将当前值作为其第一个参数,并将给定的update作为第二个参数。
	 public final int getAndAccumulate(int x,
                                      IntBinaryOperator accumulatorFunction) {
        int prev, next;
        do {
            prev = get();
            next = accumulatorFunction.applyAsInt(prev, x);
        } while (!compareAndSet(prev, next));
        return prev;
    }
    //同getAndAccumulate,但返回更新后的值
     public final int accumulateAndGet(int x,
                                      IntBinaryOperator accumulatorFunction) {
        int prev, next;
        do {
            prev = get();
            next = accumulatorFunction.applyAsInt(prev, x);
        } while (!compareAndSet(prev, next));
        return next;
    }
}

可以看到,底层都是使用CAS+自旋的乐观锁机制。
使用演示:

public class UseAtomicInt {
    static AtomicInteger atomicInteger = new AtomicInteger(10);

    public static void main(String[] args) {
        //返回旧值10
        System.out.println(atomicInteger.getAndIncrement());
        //返回新值12
        System.out.println(atomicInteger.incrementAndGet());
        atomicInteger.compareAndSet(12,1);
        //加24 返回25
        System.out.println(atomicInteger.addAndGet(24));
        //自定义函数
        IntBinarOperImpl intBinarOper=new IntBinarOperImpl();
        //传入自定义函数,传入更新值
        atomicInteger.accumulateAndGet(1, intBinarOper);
        //返回26
        System.out.println(atomicInteger.get());
    }
}
//自定义函数,实现IntBinaryOperator接口
public class IntBinarOperImpl implements IntBinaryOperator {
    @Override
    public int applyAsInt(int left, int right) {
        //简单定义原来的数加上要更新的数
        return left+right;
    }
}

AtomicIntegerArray

是提供原子的方式更新数组里的整型,还包括高级原子操作。常用方法如下:

public class AtomicIntegerArray implements java.io.Serializable {
    private static final long serialVersionUID = 2862133569453604235L;
	//同上
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    //获取数组第一个元素的偏移地址
    private static final int base = unsafe.arrayBaseOffset(int[].class);
    //存储移位个数
    private static final int shift;
    //保存的数组
    private final int[] array;

    static {
    	//获取该类型的数组中元素的大小(字节)。
        int scale = unsafe.arrayIndexScale(int[].class);
        //scale如果不是2的次幂则抛出异常
        if ((scale & (scale - 1)) != 0)
            throw new Error("data type scale not a power of two");
         //计算得到需要移位的个数,即scale的次幂数
        shift = 31 - Integer.numberOfLeadingZeros(scale);
    }
    //返回第i个元素的地址
 	private static long byteOffset(int i) {
 		//shift为偏移位数,base为第一个元素的地址,i移shift个位后+基位置得到第i个元素位置
        return ((long) i << shift) + base;
    }
    //先检测i是否越界,再调用byteOffset(i)返回
    private long checkedByteOffset(int i) {
        if (i < 0 || i >= array.length)
            throw new IndexOutOfBoundsException("index " + i);
        return byteOffset(i);
    }
    //构造初始化给定长度数组
    public AtomicIntegerArray(int length) {
        array = new int[length];
    }	
    //创建一个新AtomicIntegerArray,其长度与传入数组相同,并从给定数组中复制所有元素。所以当 AtomicIntegerArray 对内部的数组元素进行修改时,不会影响传入的数组。
    public AtomicIntegerArray(int[] array) {
        this.array = array.clone();
    }
	//返回当前位置的元素
    public final int get(int i) {
    	//调用checkedByteOffset获取当前位置的元素地址
        return getRaw(checkedByteOffset(i));
    }
	//getIntVolatile方法获取数组中offset偏移地址对应的整型field的值,支持volatile load语义。返回该值。
   private int getRaw(long offset) {
        return unsafe.getIntVolatile(array, offset);
    }
    //更新第i个位置的值,也需先计算当前位置的元素的offset
    public final void set(int i, int newValue) {
        unsafe.putIntVolatile(array, checkedByteOffset(i), newValue);
    }
    //同AtomicInteger的lazySet,这里更新第i个位置的元素
    public final void lazySet(int i, int newValue) {
        unsafe.putOrderedInt(array, checkedByteOffset(i), newValue);
    }

    public final int getAndSet(int i, int newValue) {
        return unsafe.getAndSetInt(array, checkedByteOffset(i), newValue);
    }
    
	//如果当前值等于预期值,则以原子方式将数组位置 i 的元素设置成 update 值。
    public final boolean compareAndSet(int i, int expect, int update) {
        return compareAndSetRaw(checkedByteOffset(i), expect, update);
    }

    private boolean compareAndSetRaw(long offset, int expect, int update) {
        return unsafe.compareAndSwapInt(array, offset, expect, update);
    }
	//原子的方式让第i个元素加1
    public final int getAndIncrement(int i) {
        return getAndAdd(i, 1);
    }

  	//原子的方式让第i个元素减1
    public final int getAndDecrement(int i) {
        return getAndAdd(i, -1);
    }

   	//原子的方式让第i个元素加delta
    public final int getAndAdd(int i, int delta) {
        return unsafe.getAndAddInt(array, checkedByteOffset(i), delta);
    }
	//还有其他一些方法都和Integer原子类基本一样

简单使用:

public class AtomicArray {
    static int[] value = new int[] { 1, 2,3 };
    static AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(value);
    public static void main(String[] args) {
        //返回第一个位置的元素  2
        System.out.println(atomicIntegerArray.get(1));
        //更新第1个位置的元素
        atomicIntegerArray.set(1,4);
        //返回4
        System.out.println(atomicIntegerArray.get(1));
        //设置第0个位置为3,返回更新前的值 1
        System.out.println( atomicIntegerArray.getAndSet(0, 3));
        //返回更新后的3
        System.out.println(atomicIntegerArray.get(0));
        //返回1,原数组不会变化
        System.out.println(value[0]);

        atomicIntegerArray.compareAndSet(0,3,4);
        System.out.println(atomicIntegerArray.get(0));
    }
}

更新引用类型

原子更新基本类型的 AtomicInteger,只能更新一个变量,如果要原子更新多个变量,就需要使用这个原子更新引用类型提供的类。Atomic 包提供了以下 3类。
AtomicReferenceAtomicStampedReferenceAtomicMarkableReference

AtomicReference

方法同前面几个类基本一样,但支持泛型,可传入对象,支持对对象的原子操作

AtomicStampedReference和AtomicMarkableReference

AtomicStampedReference 使用版本戳的记录每次改变后的版本号,这样的话就不会存在 ABA问题了。
AtomicMarkableReferenceAtomicStampedReference 基本相同,AtomicStampedReferencePair类 中使用 int类型的stamp 作为计数器,每次操作都更新版本,关注的的是动过几次。
AtomicMarkableReferencePair类 使用的是 boolean 类型的mark,关注的的是有没有被动过。

ABA问题演示:
/**
 * @author MaoLin Wang
 * @date 2020/3/2323:18
 */
public class ABA {
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(1);

        ThreadPoolExecutor executor=new ThreadPoolExecutor(2,3,0L, TimeUnit.MINUTES,new ArrayBlockingQueue<>(3));
        executor.execute(()->{
            System.out.println(Thread.currentThread().getName()+": value的值为:"+atomicInteger.get());
            try {
                //睡眠2s,让值从1变为2再变为1
                Thread.sleep(2000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (atomicInteger.compareAndSet(1,3)){
                System.out.println("从1修改为3成功");
            }
        });
        
        executor.execute(()->{
            int old = atomicInteger.get();
            System.out.println(Thread.currentThread().getName()+":value的值为:"+old);
            if (atomicInteger.compareAndSet(old,2)){
                System.out.println(Thread.currentThread().getName()+"将值从"+old+"修改为2");
                if (atomicInteger.compareAndSet(2,1)){
                    old=atomicInteger.get();
                    System.out.println(Thread.currentThread().getName()+"将值从"+old+"修改为1");

                }
            }

        });
        executor.shutdown();

    }
}

pool-1-thread-1: value的值为:1
pool-1-thread-2:value的值为:1
pool-1-thread-2将值从1修改为2
pool-1-thread-2将值从1修改为11修改为3成功

如上,变量从1变为2后再变为1,第一个线程认为其没有被修改过,因此将变量修改为了3,这就是典型的ABA问题。

AtomicStampedReference源码如下:

  1. 首先定义了一个Pair内部类:

     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);
            }
        }
    

    该类将元素的值(reference)和版本号(stamp)都维护在自己身上。

  2. 成员变量

    //声明Pair类型变量
    private volatile Pair<V> pair;
    //获取Unsafe
    private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
    //使用unsafe获取偏移量保存到pairOffset
    private static final long pairOffset =
            objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class);
    
      static long objectFieldOffset(sun.misc.Unsafe UNSAFE,
                                  String field, Class<?> klazz) {
        try {
            return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field));
        } catch (NoSuchFieldException e) {
            // Convert Exception to corresponding Error
            NoSuchFieldError error = new NoSuchFieldError(field);
            error.initCause(e);
            throw error;
                
    //后面两个放在源码的最后,不太好找	
    
  3. 构造方法

     public AtomicStampedReference(V initialRef, int initialStamp) {
            pair = Pair.of(initialRef, initialStamp);
        }
    

    传入初始值和初值版本戳保存到pair。

  4. 方法
    4.1 getReference()getStamp():返回元素值和返回版本戳

    public V getReference() {
            return pair.reference;
    }
     public int getStamp() {
        return pair.stamp;
    }
    

    4.2 get()
    返回当前元素的值,并将当前元素的版本号保存到传入的数组中的第一个位置

    public V get(int[] stampHolder) {
            Pair<V> pair = this.pair;
            stampHolder[0] = pair.stamp;
            return pair.reference;
        }
    

    4.3 casPair()

    //cmp:期望的pair,val:新的pair
    //CAS更新当前的pair
    private boolean casPair(Pair<V> cmp, Pair<V> val) {
            return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
     }
    

    4.4 compareAndSet()

    /**
     * 原子的修改元素的值和版本戳
     * @param 期望值
     * @param 新值
     * @param 期望版本戳
     * @param 新版本戳
     */
     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)));
        }
    
    1. 该方法首先判断期望的值和版本戳是否和当前的值和版本戳是否相同,如果不相同则直接返回false
    2. .如果相同则继续比较新的值和版本戳是否和当前值和版本戳是否相同,如果相同则直接返回true,否则对当前的pair进行CAS操作。

    4.5 weakCompareAndSet()

    // 同compareAndSet,但可能不能保证原子性
    public boolean weakCompareAndSet(V   expectedReference,
                                         V   newReference,
                                         int expectedStamp,
                                         int newStamp) {
            return compareAndSet(expectedReference, newReference,
                                 expectedStamp, newStamp);
        }
    

    其代码同compareAndSet无差别,但在JDK9中,两个方法都添加了@HotSpotIntrinsicCandidate注解,使用该注解的方法在JVM中都有一套基于CPU指令的高效的实现,且会在运行时会替代JDK的源码实现,从而获得更高的效率。即HotSpot可能会手动实现这个方法。参考https://blog.csdn.net/lzcaqde/article/details/80868854的解读。
    4.6set()

    /**
     * 源码解释无条件更新当前元素的值和版本戳
     * @param newReference 新值
     * @param newStamp 新版本戳
     */
    public void set(V newReference, int newStamp) {
            Pair<V> current = pair;
            if (newReference != current.reference || newStamp != current.stamp)
                this.pair = Pair.of(newReference, newStamp);
        }
    

    4.6attemptStamp()

    如果引用对象为期望值,则重新设置新的版本戳。

    
     public boolean attemptStamp(V expectedReference, int newStamp) {
            Pair<V> current = pair;
            return
                expectedReference == current.reference &&
                (newStamp == current.stamp ||
                 casPair(current, Pair.of(expectedReference, newStamp)));
        }
    

示例:解决ABA问题

/**
 * 类说明:演示带版本戳的原子操作类解决ABA
 * @author MaoLin Wang
 * @date 2020/3/2324:11
 */
public class UseAtomicStampedReference {
    /**
     * 设置初始值和初始版本戳
     */
    static AtomicStampedReference<String> asr
            = new AtomicStampedReference("wml",0);

    public static void main(String[] args) throws InterruptedException {


       new Thread(() -> {
            int oldStamp = asr.getStamp();
            String oldReference = asr.getReference();
            System.out.println(Thread.currentThread().getName()+":当前变量值:"
                    +oldReference + ",当前版本戳:" + oldStamp );
            try {
                Thread.sleep(3000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //更新引用对象值和版本戳
            if ( asr.compareAndSet(oldReference, "wml222", oldStamp, oldStamp + 1)){
                System.out.println(Thread.currentThread().getName()+": 成功从版本:"+oldStamp+"的"+oldReference+"更新为->>>版本:"+(oldStamp+1)+",新值:wml222");
            }else {
                System.out.println(Thread.currentThread().getName()+": 从版本:"+oldStamp+"的"+oldReference+"更新失败!!!!");
            }

        },"线程1").start();

        new Thread(() -> {
            String oldReference = asr.getReference();
            int oldStamp = asr.getStamp();
            System.out.println(Thread.currentThread().getName()+":当前变量值:"
                    +oldReference + ",当前版本戳:" +oldStamp );
            //更新引用对象值和版本戳
            if (asr.compareAndSet(oldReference, "wml333", oldStamp, oldStamp + 1)){
                System.out.println(Thread.currentThread().getName()+": 成功从版本:"+oldStamp+"的"+oldReference+"更新为->>>版本:"+(oldStamp+1)+",新值:wml222");
                oldReference = asr.getReference();
                oldStamp = asr.getStamp();
                if (asr.compareAndSet(oldReference, "wml", oldStamp, oldStamp + 1)){

                    System.out.println(Thread.currentThread().getName()+": 成功从版本:"+oldStamp+"的"+oldReference+"更新为->>>版本:"+(oldStamp+1)+",新值:wml222");

                }
            }

        },"线程2").start();

    }
}


线程1:当前变量值:wml,当前版本戳:0
线程2:当前变量值:wml,当前版本戳:0
线程2: 成功从版本:0的wml更新为->>>版本:1,新值:wml222
线程2: 成功从版本:1的wml333更新为->>>版本:2,新值:wml222
线程1: 从版本:0的wml更新失败!!!!

线程1想要更新值,发现版本戳已改变,因此修改失败,解决的ABA问题。

原子更新字段类

atomic包提供了AtomicIntegerFieldUpdaterAtomicLongFieldUpdaterAtomicReferenceFieldUpdater三个类实现原子的更新某个类的某个字段。
AtomicReferenceFieldUpdater为例:
更新字段类需要两步:
1.由于原子更新字段类都是抽象类,每次必须使用静态方法 newUpdater()创建一个更新器,并且传入要更新的类,字段类型和字段名。
2.更新类的字段(属性)必须使用 public volatile
修饰符。
其他注意点:
1.变量不可使用static、final关键字。
2.变量的描述符类型必须与调用者一致。否则调用者不能调用变量也就不能通过反射操作保证原子性。

每个更新字段类都是一个抽象类,内部有一个AtomicXXXXFieldUpdaterImpl实现类,相关代码如下:

 private static final class AtomicReferenceFieldUpdaterImpl<T,V>
        extends AtomicReferenceFieldUpdater<T,V> {
        // 获取Unsafe
	    private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
	    // 当前变量的内存偏移量
	    private final long offset;
	    // 如果要操作的类的字段被protected修饰,则cclass为调用者类的class对象,否则cclass为tclass。
	    private final Class<?> cclass;
	    // 要操作的类的class对象
	    private final Class<T> tclass;
        // 要操作的类字段的class对象
        private final Class<V> vclass;
 
    /**
     * @param tclass 即上面的tclass
     * @param vclass 要操作的类字段的class对象
     * @param fieldName 被操作的字段名
     * @param caller 调用者类的class对象
     */       
AtomicReferenceFieldUpdaterImpl(final Class<T> tclass,
                                        final Class<V> vclass,
                                        final String fieldName,
                                        final Class<?> caller) {
          	 //原子更新的字段
            final Field field;
            //原子更新字段的class对象
            final Class<?> fieldClass;
            //原子更新字段的修饰符
            final int modifiers;
            try {
            	//利用反射机制获取tclass的field
                field = AccessController.doPrivileged(
                    new PrivilegedExceptionAction<Field>() {
                        public Field run() throws NoSuchFieldException {
                            return tclass.getDeclaredField(fieldName);
                        }
                    });
                //获取该字段的修饰符
                modifiers = field.getModifiers();
                //检查该字段的访问权限,无法访问则抛出对应异常信息
                sun.reflect.misc.ReflectUtil.ensureMemberAccess(
                    caller, tclass, null, modifiers);
                //获取对应class的类加载器
                ClassLoader cl = tclass.getClassLoader();
                ClassLoader ccl = caller.getClassLoader();
                if ((ccl != null) && (ccl != cl) &&
                    ((cl == null) || !isAncestor(cl, ccl))) {
                    sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass);
                }
                fieldClass = field.getType();
            } catch (PrivilegedActionException pae) {
                throw new RuntimeException(pae.getException());
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }

            if (vclass != fieldClass)
                throw new ClassCastException();
            if (vclass.isPrimitive())
                throw new IllegalArgumentException("Must be reference type");

            if (!Modifier.isVolatile(modifiers))
                throw new IllegalArgumentException("Must be volatile type");

            //首先判断字段修饰符是否为protected
            // 再判断tclass和caller是否相同或者是另一个类的子类
            this.cclass = (Modifier.isProtected(modifiers) &&
                           tclass.isAssignableFrom(caller) &&
                           !isSamePackage(tclass, caller))
                          ? caller : tclass;
            this.tclass = tclass;
            this.vclass = vclass;
            this.offset = U.objectFieldOffset(field);
        }
        //如果可以在第一个类加载器的委托链中找到第二个类加载器,则返回true
         private static boolean isAncestor(ClassLoader first, ClassLoader second) {
            ClassLoader acl = first;
            do {
                acl = acl.getParent();
                if (second == acl) {
                    return true;
                }
            } while (acl != null);
            return false;
        }

原子更新类的方法同atomic包下的其他类基本相同,但是该类在进行CAS前,需要先进行类型检查,如下:

	//检查目标参数是否是类的实例。失败时,抛出原因。
  private final void accessCheck(T obj) {
            if (!cclass.isInstance(obj))
                throwAccessCheckException(obj);
 }
 //检查目标参数是否是对应字段的类型,如果不是则抛出异常
 private final void valueCheck(V v) {
            if (v != null && !(vclass.isInstance(v)))
                throwCCE();
        }     
 public final boolean compareAndSet(T obj, V expect, V update) {
            accessCheck(obj);
            valueCheck(update);
            return U.compareAndSwapObject(obj, offset, expect, update);
 }

使用示例:

public class AtomicReferenceFieldUpdaterDemo {

    /**
     * 定义AtomicReferenceFieldUpdater,传入要操作的类User,以及原子操作的字段age的class对象Integer.class和字段名age,这里字段必须为引用类型,如果是int类型的则需使用
     */
    static AtomicReferenceFieldUpdater arf = AtomicReferenceFieldUpdater.newUpdater(User.class, Integer.class, "age");
	/**
     * 定义AtomicIntegerFieldUpdater,传入要操作的类User,以及原子操作的字段名grade
     */
    static AtomicIntegerFieldUpdater aif=AtomicIntegerFieldUpdater.newUpdater(User.class,"grade");


     public static void main(String[] args) throws InterruptedException {

        User user = new User();
        user.age=19;
        user.grade=0;
        //CAS修改为20
        arf.compareAndSet(user, 19, 20);
        //输出20
        System.out.println(arf.get(user));
        //修改为21,返回修改前的 20
        System.out.println(arf.getAndSet(user,21));
        //修改后为21
        System.out.println(user.age);

        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(500, 1000, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingDeque());
        //开500个线程增加grade字段
        for (int i = 0; i < 500; i++) {

                    poolExecutor.execute(()->{
                        aif.incrementAndGet(user);

                    });
        }
        poolExecutor.shutdown();
        Thread.sleep(2000L);
        System.out.println("增加后的grade为:"+aif.get(user));
        aif.compareAndSet(user,500,1000);
        System.out.println("修改后的grade为"+aif.get(user));

    }
}

class User {
   protected volatile Integer age;
   protected volatile int grade;

}

结果:

20
20
21
增加后的grade为:500
修改后的grade为1000

参考:
https://zhuanlan.zhihu.com/p/65240318

下一篇:显示锁和AQS

发布了75 篇原创文章 · 获赞 13 · 访问量 8369

猜你喜欢

转载自blog.csdn.net/weixin_43696529/article/details/104106247