zoukankan      html  css  js  c++  java
  • juc学习三(CAS中ABA问题产生和解决)

    ABA问题的产生

    CAS会导致“ABA问题”。

    CAS算法实现一个重要前提需要取出内存中某时刻的数据并在当下时刻比较并替换,那么在这个时间差类会导致数据的变化。

    比如说一个线程1从内存位置V中取出A,这时候另一个线程2也从内存中取出A,并且线程2进行了一些操作将值变成了B,然后线程2又将V位置的数据 变成了A,这时候线程1进行CAS操作发现内存中仍然是A,然后线程1操作成功。只关注开始和结尾,不关心中间过程。

    尽管线程1的CAS操作成功,但是不代表这个过程就是没有问题的。

    演示ABA问题的产生

    /**
     * 演示ABA问题产生
     */
    public class ABADemo1 {
    
        public static AtomicReference<Integer> atomicReference=new AtomicReference<>(100);
    
        public static void main(String[] args) {
            System.out.println("------------ABA问题的产生----------------");
            new Thread(()->{
                //100改101再改回100
                atomicReference.compareAndSet(100,101);
                atomicReference.compareAndSet(101,100);
            },"t1").start();
    
            new Thread(()->{
                //暂停1s,保证上面的t1完成一次ABA操作
                try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) { e.printStackTrace();}
                //只看结果值,不看过程
                System.out.println(atomicReference.compareAndSet(100,2019)+"---"+atomicReference.get());;
            },"t2").start();
        }
    }

    ABA问题解决

    理解原子引用+新增一种机制,那就是修改版本号(类似时间戳)

     先了大概解下原子引用

    java.util.concurrent.atomic的atomic包里AtomicReference。
    AtomicReference是作用是对”对象”进行原子操作。提供了一种读和写都是原子性的对象引用变量。原子意味着多个线程试图改变同一个AtomicReference(例如比较和交换操作)将不会使得AtomicReference处于不一致的状态。

    /**
     * AtomicReference原子引用
     */
    public class AtomicReferenceDemo {
        
        public static void main(String[] args) {
            User user1= new User("张三",22);
            User user2= new User("李四",23);
    
            AtomicReference<User> atomicReference=new AtomicReference<>();
            //设置主物理内存值为user1
            atomicReference.set(user1);
            //现在比较并交换
            System.out.println(atomicReference.compareAndSet(user1,user2)+"-----"+atomicReference.get());
            System.out.println(atomicReference.compareAndSet(user1,user2)+"-----"+atomicReference.get());
        }
    }
    
    @Data
    @AllArgsConstructor
    class User{
        private String name;
        private int age;
    }

    然后再用JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。

    简单说就是加个类似时间戳的标志,也就是说每一次修改只需要设置不同的版本好即可。如果当前引用 == 预期引用,并且当前标志等于预期标志,则以原子方式将该引用和该标志的值设置为给定的更新值。

    演示ABA问题解决

    /**
     * ABA问题解决AtomicStampedReference
     */
    public class ABADemo2 {
    
        public static AtomicStampedReference<Integer> atomicStampedReference=new AtomicStampedReference<>(100,1);
    
        public static void main(String[] args) {
            System.out.println("------------ABA问题的解决----------------");
            new Thread(()->{
                //获取版本号
                int stamp=atomicStampedReference.getStamp();
                System.out.println(Thread.currentThread().getName()+" 第1次版本号:"+stamp+
                        "	 当前值:"+atomicStampedReference.getReference());
                //暂停1s为了让t4获取到同一版本号
                try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) { e.printStackTrace();}
                atomicStampedReference.compareAndSet(100,101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
                System.out.println(Thread.currentThread().getName()+" 第2次版本号:"+atomicStampedReference.getStamp()+
                        "	当前值:"+atomicStampedReference.getReference());
                atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
                System.out.println(Thread.currentThread().getName()+" 第3次版本号:"+atomicStampedReference.getStamp()+
                        "	当前值:"+atomicStampedReference.getReference());
            },"t3").start();
    
            new Thread(()->{
                //获取版本号
                int stamp=atomicStampedReference.getStamp();
                System.out.println(Thread.currentThread().getName()+" 第1次版本号:"+stamp+"	当前值:"+atomicStampedReference.getReference());
                //暂停3s,保证上面的t3完成一次ABA操作
                try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) { e.printStackTrace();}
                //t4还是用傻傻地用上面获取的版本号
                boolean result=atomicStampedReference.compareAndSet(100,2019,stamp,stamp+1);
                System.out.println(Thread.currentThread().getName()+" 修改结果:"+result);
                System.out.println(Thread.currentThread().getName()+"当前版本号:"+atomicStampedReference.getStamp()+
                        "	当前最新值"+atomicStampedReference.getReference());
            },"t4").start();
        }
    }
    

      

      

  • 相关阅读:
    C# NPOI 导入与导出Excel文档 兼容xlsx, xls(xf13中已经引用了xlsx的npoi)
    ASP.Net超时时间已到解决办法-
    解决SqlDataSource连接超时的问题
    SqlDataSource控件超时的困惑
    Redis+Keepalived
    Linux Ubuntu 16.04 安装步骤+远程环境
    JDK 安装
    Maven 安装
    CAT 默认密码修改
    CAT 监控搭建
  • 原文地址:https://www.cnblogs.com/mabaoying/p/13094173.html
Copyright © 2011-2022 走看看