• 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)));
    }
     
  • 相关阅读:
    适配器模式
    注册模式
    工厂模式
    策略模式和注入控制反转
    验签
    php中加密和解密
    asp.net textbox keyup事件触发后台的textchange事件
    jquery $.post 返回json数据
    网页播放音频、视频文件——基于web的html 5的音乐播放器(转载)
    c# 柱状图(转载)
  • 原文地址:https://www.cnblogs.com/bj-xiaodao/p/10799327.html
Copyright © 2020-2023  润新知