面试准备-- ABA问题解决方案

上一篇文章我们介绍了原子类 AtomicInteger 我们知道了该类留下的一个逻辑漏洞–ABA 问题。

ABA 问题的终极解决方案就是 – 版本号。和我们数据库中使用乐观锁加一个 version 字段类似,数据库是每次插入时都检验一下版本号是否一致。而我们解决 ABA 问题的核心也是一样的,修改前检验一下当前版本号是否一致。

JDK 中 AtomicStampedReference/AtomicMarkableReference 这两个类就是使用版本号的方式来解决 AtomicInteger 的 ABA 问题。

两者区别:

AtomicStampedReference AtomicMarkableReference
字段 stamp mark

AtomicStampedReference 中的版本号 stamp 是一个 int 类型

AtomicMarkableReference 中的版本号 mark 则是一个 boolean 类型

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

我们再看看相关的构造

	 /**
     * 构造器,初始化时将内部类也初始化
     */
 	public AtomicStampedReference(V initialRef, int initialStamp) {
        pair = Pair.of(initialRef, initialStamp);
    }

    /**
     * 获得当前的对象引用
     */
    public V getReference() {
        return pair.reference;
    }

    /**
     * 获得当前版本号
     */
    public int getStamp() {
        return pair.stamp;
    }

    /**
     * 获得版本号和对象引用
     * 这里有个小知识,下面讲解
     */
    public V get(int[] stampHolder) {
        Pair<V> pair = this.pair;
        stampHolder[0] = pair.stamp;
        return pair.reference;
    }

这里讲解一下为什么在 get 方法要使用 int[] 类型的参数。如果对 java 堆栈有一些认识的同学应该很快就懂,如果不知道可以去学习一下 java 基础。

基本类型在栈中执行完毕后就被释放掉了,而使用对象数组的目的可以进行引用传递,将数组第一位的引用指向当前的版本号,这样我们就可以拿到当前版本号了。

核心方法:*

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

AtomicMarkableReference :

这一个整个类的代码感觉就是复制上面的,改了一个字段名和类型,修改一下方法的名字,其他基本没变化。

 private static class Pair<T> {
        final T reference;
        //AtomicStampedReference 使用的是 int 类型
        final boolean mark;
        private Pair(T reference, boolean mark) {
            this.reference = reference;
            this.mark = mark;
        }
        static <T> Pair<T> of(T reference, boolean mark) {
            return new Pair<T>(reference, mark);
        }
    }

AtomicMarkableReference 的好处是只有两种情况,是 true 和 false,感觉上就像是一个标记,int 版本号是根据数值判断,而这个则是根据 true,false 来判断是否被修改过。

总结:
两个类都是为了解决 ABA 问题而出的解决方案,但是具体我也不知道能做些什么。当然,如果是面试的话,我想这两个方案都可以提及即可。

有兴趣的同学欢迎关注公众号面试准备-- ABA问题解决方案_第1张图片

你可能感兴趣的:(面试准备)