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