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();
        }
  • 相关阅读:
    POJ 1251 Jungle Roads
    1111 Online Map (30 分)
    1122 Hamiltonian Cycle (25 分)
    POJ 2560 Freckles
    1087 All Roads Lead to Rome (30 分)
    1072 Gas Station (30 分)
    1018 Public Bike Management (30 分)
    1030 Travel Plan (30 分)
    22. bootstrap组件#巨幕和旋转图标
    3. Spring配置文件
  • 原文地址:https://www.cnblogs.com/starcrm/p/12711591.html
Copyright © 2011-2022 走看看