atomicReference 使用和AtomicStampedReference 解决ABA的问题


atomicReference 可以保证对象的原子操作.

public static void main(String[] args) {


            AtomicReference<Simple> atomic = new AtomicReference<>(new Simple("xiaodao",23));


            System.out.println(atomic.get());
            boolean result = atomic.compareAndSet(atomic.get(), new Simple("bbb", 90));

            System.out.println(result);




        }

        static class Simple{
            private String name;
            private int  age;

            public Simple(String name, int age) {
                this.name = name;
                this.age = age;
            }


            public String getName() {
                return name;
            }

            public void setName(String name) {
                this.name = name;
            }

            public int getAge() {
                return age;
            }

            public void setAge(int age) {
                this.age = age;
            }
        }

View Code

CAS

cas带来的好处

1.可见性

2.有序性

3.原子性

volatile修饰的变量,保证前俩者

CAS算法,也就是cpu级别的同步指令,相当于乐观锁,它可以探测到其他线程对共享数据的变化情况\

cas带来一个ABA问题

什么是ABA呢?

就是俩个线程同事操作,有可能有一个线程已经处理结束,那么第一个线程中间又一次fail操作,会在这个结果上在进行计算

t1 t2

A A->B->A

A->C

那我们来看下维基百科上面的形象描述:

你拿着一个装满钱的手提箱在飞机场,此时过来了一个火辣性感的美女,然后她很暖昧地挑逗着你,并趁你不注意的时候,把用一个一模一样的手提箱和你那装满钱的箱子调了个包,然后就离开了,你看到你的手提箱还在那,于是就提着手提箱去赶飞机去了。

如何解决ABA的问题呢?

在数据库中是使用乐观锁来解决的这问题version一直在变化

在多线程中我们有一个类 AtomicStampedReference可以这样解决

public class AtomicStampedReferenceTest {

        private static  AtomicStampedReference<Integer> atomic = new AtomicStampedReference<>(100,0);

        public static void main(String[] args) {
            new Thread(()->{
                try {
                    TimeUnit.SECONDS.sleep(1);
                    //和乐观相似,第一个stamp相当于 version 每次加1 如果别的线程修改过就是1了.第一个线程0就对比不成功
                  boolean sucess =   atomic.compareAndSet(100,101,atomic.getStamp(),atomic.getStamp()+1);
                    System.out.println(sucess);
                    sucess =   atomic.compareAndSet(101,100,atomic.getStamp(),atomic.getStamp()+1);
                    System.out.println(sucess);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();


            new Thread(()->{
                try {
                    int stamp = atomic.getStamp();
                    System.out.println("before:   "+stamp);//这里获取到的stamp是1 
                    TimeUnit.SECONDS.sleep(2);
                    int stamp1 = atomic.getStamp();
                    System.out.println("第一个线程的 stamp "+ stamp1);//这里第一个线程的stamp已经变成2 了
                    //和乐观相似,第一个stamp相当于 version 每次加1 如果别的线程修改过就是1了.第一个线程0就对比不成功
                    boolean b = atomic.compareAndSet(100, 101, stamp, stamp + 1);
                    System.out.println("2线程= "+b);
                    atomic.compareAndSet(101,100,stamp,stamp+1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();

            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }


        }
    }

源码:

我=copy了一点 也不算太复杂,就是初始化的时候放进入一个值,和一个版本号,

然后进行compareAndSet 的话,就是比对预期的值和当前的值,还有预期的版本号和当前的版本号

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;

        /**
         * Creates a new {@code AtomicStampedReference} with the given
         * initial values.
         *
         * @param initialRef the initial reference
         * @param initialStamp the initial stamp
         */
        public AtomicStampedReference(V initialRef, int initialStamp) {
            pair = Pair.of(initialRef, initialStamp);
        }  



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


原文链接:https://www.cnblogs.com/bj-xiaodao/p/10799327.html