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();
        }
  • 相关阅读:
    很简单的企业管理器我写程序的方式,几个自定义控件。
    当OO遇到了持久化?!
    [自定义服务器控件] 第一步:文本框。
    [面向过程——老酒换新瓶] (一)开篇:是面向过程还是面向对象?
    个人理财小助手 —— 设计思路、功能说明
    《Head First 设计模式》 终于出中文版了。
    其实添加数据也可以这样简单——表单的第一步抽象(针对数据访问层)《怪怪设计论: 抽象无处不在 》有感
    基类、接口的应用——表单控件:一次添加、修改一条记录,一次修改多条记录。(上)
    其实添加数据也可以这样简单——表单的第三步抽象(针对UI及后置代码)
    转帖:客户端表单通用验证checkForm(oForm) js版
  • 原文地址:https://www.cnblogs.com/starcrm/p/12711591.html
Copyright © 2011-2022 走看看