zoukankan      html  css  js  c++  java
  • Java底层类和源码分析系列-AtomicStampedReference解决ABA问题

    在原子类持续累加或累减时,比如AtomicInteger的incrementAndGet时,是不存在ABA问题的,但compareAndSet或者updateAndGet是可能存在ABA问题,像AtomicBoolean或AtomicLong等这样的变量在多线程修改时,也都存在ABA的问题。
    为了理解ABA,下面的例子,针对线程1来说,第一次的A也就是1和第二次的A是另外修改过的1,实际上并不是同一个A(1)。

    public static void main(String[] args) {
            AtomicInteger atomicInteger = new AtomicInteger(1);
            new Thread(()->{
                int value = atomicInteger.get();
                System.out.println("thread 1 read value: " + value);
                // 阻塞1s
                LockSupport.parkNanos(1000000000L);
                if (atomicInteger.compareAndSet(value, 3)) {
                    System.out.println("thread 1 update from " + value + " to 3");
                } else {
                    System.out.println("thread 1 update fail!");
                }
            }).start();
            new Thread(()->{
                int value = atomicInteger.get();
                System.out.println("thread 2 read value: " + value);
                if (atomicInteger.compareAndSet(value, 2)) {
                    System.out.println("thread 2 update from " + value + " to 2");
                    // do sth
                    value = atomicInteger.get();
                    System.out.println("thread 2 read value: " + value);
                    if (atomicInteger.compareAndSet(value, 1)) {
                        System.out.println("thread 2 update from " + value + " to 1");
                    }
                }
            }).start();
        }

    为了解决这个问题,引入了AtomicStampedReference。

    内部类

        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;
    private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
    private static final long pairOffset =
        objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class);

    构造方法

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


    compareAndSet()方法

    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) ||
              // 构造新的Pair对象并CAS更新
             casPair(current, Pair.of(newReference, newStamp)));
    }
    private boolean casPair(Pair<V> cmp, Pair<V> val) {
        // 调用Unsafe的compareAndSwapObject()方法CAS更新pair的引用为新引用
        return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
    }

    解决上个ABA的问题的版本:

    private static void testStamp() {
            AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1, 1);
            new Thread(()->{
                int[] stampHolder = new int[1];
                int value = atomicStampedReference.get(stampHolder);
                int stamp = stampHolder[0];
                System.out.println("thread 1 read value: " + value + ", stamp: " + stamp);
                // 阻塞1s
                LockSupport.parkNanos(1000000000L);
                if (atomicStampedReference.compareAndSet(value, 3, stamp, stamp + 1)) {
                    System.out.println("thread 1 update from " + value + " to 3");
                } else {
                    System.out.println("thread 1 update fail!");
                }
            }).start();
            new Thread(()->{
                int[] stampHolder = new int[1];
                int value = atomicStampedReference.get(stampHolder);
                int stamp = stampHolder[0];
                System.out.println("thread 2 read value: " + value + ", stamp: " + stamp);
                if (atomicStampedReference.compareAndSet(value, 2, stamp, stamp + 1)) {
                    System.out.println("thread 2 update from " + value + " to 2");
                    // do sth
                    value = atomicStampedReference.get(stampHolder);
                    stamp = stampHolder[0];
                    System.out.println("thread 2 read value: " + value + ", stamp: " + stamp);
                    if (atomicStampedReference.compareAndSet(value, 1, stamp, stamp + 1)) {
                        System.out.println("thread 2 update from " + value + " to 1");
                    }
                }
            }).start();
        }
  • 相关阅读:
    Python笔试题(递归)
    MYSQL经典面试题
    Linux常用命令
    HTTP协议相关面试题
    Flask面试题
    史上最全DVWA 笔记
    ssh root Permission denied
    odoo Reference 选择model 然后选择record
    定体, 定压, 定温, 绝热 Q E A 公式
    Vmware Bluetooth
  • 原文地址:https://www.cnblogs.com/starcrm/p/12711591.html
Copyright © 2011-2022 走看看