绍兴自驾游:原子类的ABA问题

admin 1周前 (09-21) 科技 41 2

原子类AtoMicInteger的ABA问题

连环套路

从AtomicInteger引出下面的问题

CAS -> Unsafe -> CAS底层头脑 -> ABA -> 原子引用更新 -> 若何规避ABA问题

ABA问题是什么

狸猫换

假设现在有两个线程,分别是T1 和 T2,然后T1执行某个作的时间为10秒,T2执行某个时间的操作是2秒,最最先AB两个线程,分别从主内存中获取A值,然则由于B的执行速率更快,他先把A的值改成B,然后在修改成A,然后执行完毕,T1线程在10秒后,执行完毕,判断内存中的值为A,而且和自己预期的值一样,它就以为没有人更改了主内存中的值,就快乐的修改成B,然则现实上 可能中心履历了 ABCDEFA 这个变换,也就是中心的值履历了狸猫换太子。

以是ABA问题就是,在举行获取主内存值的时刻,该内存值在我们写入主内存的时刻,已经被修改了N次,然则最终又改成原来的值了

CAS导致ABA问题

CAS算法实现了一个主要的条件,需要取出内存中某时刻的数据,并在当下时刻对照并替换,那么这个时间差会导致数据的转变。

比如说一个线程one从内存位置V中取出A,这时刻另外一个线程two也从内存中取出A,而且线程two举行了一些操作将值酿成了B,然后线程two又将V位置的数据酿成A,这时刻线程one举行CAS操作发现内存中仍然是A,然后线程one操作乐成

只管线程one的CAS操作乐成,然则不代表这个历程就是没有问题的

ABA问题

CAS只管开头和末端,也就是头和尾是一样,那就修改乐成,中心的这个历程,可能会被人修悔改

原子引用

原子引用实在和原子包装类是差不的观点,就是将一个java类,用原子引用类举行包装起来,那么这个类就具备了原子性

/**
 * 原子类引用
 */
@Data
@AllArgsConstructor
class User {
    String userName;
    int age;
}
public class AtomicReferenceDemo {

    public static void main(String[] args) {

        User aaa = new User("aaa", 20);
        User bbb = new User("bbb", 30);

        // 建立原子引用包装类
        AtomicReference<User> atomicReference = new AtomicReference<>();
        // 现在主物理内存的共享变量,为aaa
        atomicReference.set(aaa);

        // 对照并交流,若是现在主物理内存的值为aaa,那么交流成bbb
        System.out.println(atomicReference.compareAndSet(aaa, bbb) + "\t " + atomicReference.get().toString());
        // 对照并交流,现在主物理内存的值是bbb了,然则预期为aaa,因此交流失败
        System.out.println(atomicReference.compareAndSet(aaa, bbb) + "\t " + atomicReference.get().toString());
    }
}

基于原子引用的ABA问题

我们首先建立了两个线程,然后T1线程,执行一次ABA的操作,T2线程在一秒后修改主内存的值

/**
 * 基于CAS引出ABA问题
 */
public class ABADemo {

    static AtomicReference<Integer> atomicReference = new AtomicReference<Integer>(100);

    public static void main(String[] args) {
        new Thread(()->{
            // 把100 改成 127 然后在改成100,也就是ABA
            atomicReference.compareAndSet(100, 127);
            //特别强调在AtomicReference(Integer)中value超出-128~127,会天生一个新的工具而造成无法修改
            //然则在AtomicInteger中则不会存在这样的问题
            atomicReference.compareAndSet(127, 100);
        },"t1").start();

        new Thread(()->{
            try {
                // 睡眠一秒,保证t1线程,完成了ABA操作
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 把100 改成 127 然后在改成100,也就是ABA
            System.out.println(atomicReference.compareAndSet(100, 2021)+"\t"+atomicReference.get());
        },"t2").start();
    }
}

我们发现,它能够乐成的修改,这就是ABA问题

解决ABA问题

新增一种机制,也就是修改版本号,类似于时间戳的观点

T1: 100 1 2020 2

T2: 100 1 127 2 100 3

若是T1修改的时刻,版本号为2,落后于现在的版本号3,以是要重新获取最新值,这里就提出了一个使用时间戳版本号,来解决ABA问题的思绪

AtomicStampedReference

时间戳原子引用,来这里应用于版本号的更新,也就是每次更新的时刻,需要对照期望值和当前值,以及期望版本号和当前版本号

/**
 * 基于CAS引出ABA问题并接纳AtomicStampedReference解决
 */
public class ABADemo {

    static AtomicReference<Integer> atomicReference = new AtomicReference<Integer>(100);

    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);

    public static void main(String[] args) {
        System.out.println("============以下是ABA问题的发生==========");

        new Thread(() -> {
            // 把100 改成 101 然后在改成100,也就是ABA
            atomicReference.compareAndSet(100, 127);
            atomicReference.compareAndSet(127, 100);
        }, "t1").start();

        new Thread(() -> {
            try {
                // 睡眠一秒,保证t1线程,完成了ABA操作
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 把100 改成 101 然后在改成100,也就是ABA
            System.out.println(atomicReference.compareAndSet(100, 2020) + "\t" + atomicReference.get());

        }, "t2").start();

        //main线程和gc线程之外若是另有线程就处于守候
        while (Thread.actIVeCount() > 2) {
            Thread.yield();
        }
        System.out.println("============以下是ABA问题的解决==========");

        new Thread(() -> {

            // 获取版本号
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + "\t 第一次版本号" + stamp);

            // 暂停t3一秒钟
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // 传入4个值,期望值,更新值,期望版本号,更新版本号
            atomicStampedReference.compareAndSet(100, 127, atomicStampedReference.getStamp(), atomicStampedReference.getStamp()+1);

            System.out.println(Thread.currentThread().getName() + "\t 第二次版本号" + atomicStampedReference.getStamp());

            atomicStampedReference.compareAndSet(127, 100, atomicStampedReference.getStamp(), atomicStampedReference.getStamp()+1);

            System.out.println(Thread.currentThread().getName() + "\t 第三次版本号" + atomicStampedReference.getStamp());

        }, "t3").start();

        new Thread(() -> {

            // 获取版本号
            int stamp = atomicStampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + "\t 第一次版本号" + stamp);

            // 暂停t4 3秒钟,保证t3线程也举行一次ABA问题
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            boolean result = atomicStampedReference.compareAndSet(100, 2020, stamp, stamp+1);

            System.out.println(Thread.currentThread().getName() + "\t 修改乐成否:" + result + "\t 当前最新现实版本号:" + atomicStampedReference.getStamp());

            System.out.println(Thread.currentThread().getName() + "\t 当前现实最新值" + atomicStampedReference.getReference());


        }, "t4").start();
    }
}

运行效果为:

绍兴自驾游:原子类的ABA问题  第1张

我们能够发现,线程t3,在举行ABA操作后,版本号调换成了3,而线程t4在举行操作的时刻,就泛起操作失败了,由于版本号和当初拿到的不一样

,

诚信在线

诚信在线(www.cx11yl.cn)现已开放诚信在线手机版下载。游戏公平、公开、公正,用实力赢取信誉。

Sunbet声明:该文看法仅代表作者自己,与本平台无关。转载请注明:绍兴自驾游:原子类的ABA问题

网友评论

  • (*)

最新评论

  • 皇冠APP下载 2020-09-21 00:16:04 回复

    Allbet官网欢迎进入Allbet官网(Allbet Game):www.aLLbetgame.us,欧博官网是欧博集团的官方网站。欧博官网开放Allbet注册、Allbe代理、Allbet电脑客户端、Allbet手机版下载等业务。推荐给好友了

    1
  • 皇冠APP下载 2020-09-21 00:16:11 回复

    Allbet官网欢迎进入Allbet官网(Allbet Game):www.aLLbetgame.us,欧博官网是欧博集团的官方网站。欧博官网开放Allbet注册、Allbe代理、Allbet电脑客户端、Allbet手机版下载等业务。推荐给好友了

    2

站点信息

  • 文章总数:621
  • 页面总数:0
  • 分类总数:8
  • 标签总数:1120
  • 评论总数:255
  • 浏览总数:6874