Java--JUC--CAS问题及解决方式

 

  1. CAS:
    1. package com.model.CAS;
      
      import java.util.concurrent.atomic.AtomicInteger;
      
      /**
       * @Description:测试类
       * @Author: 张紫韩
       * @Crete 2021/6/10 13:47
       */
      public class CASDemo {
          /**
           * 什么是CAS: compareAndSet  比较并交换
           * 总结:比较当前工作内存的值和主内存的值,如果这个值是期望的,则执行操作,如果不是就一直循环(自旋锁)
           * 缺点:
           * 1.循环耗时
           * 2.一次性只能保证一个共享变量的原子性
           * 3.会产生ABA问题
           * */
      
          public static void main(String[] args) {
              //原子类 java.until.concurrent.atomic
              AtomicInteger atomicInteger = new AtomicInteger(2021);
              //expect :期望  update:更新
              //如果是2021 就改为2022并返回true,如果不是2021就不会更新为2022并返回false  ,CAS是cpu的原语
              System.out.println(atomicInteger.compareAndSet(2021, 2022)); //返回值为ture和false
              System.out.println(atomicInteger.get());
              System.out.println(atomicInteger.compareAndSet(2021, 2022));
              System.out.println(atomicInteger.get());
      
              atomicInteger.getAndIncrement(); // 相当于是++
      
              /**
               *   atomicInteger.getAndIncrement(); 是如何实现的呢?
               *
               *   通过源码我们可以看到,他是通过调用 unsafe.getAndAddInt(this, valueOffset, 1)来实现加一的,
               *   我们在进入到 unsafe类中,他调用了更底层的compareAndSwapInt(var1, var2, var5, var5 + var4))进行加一
               *   而public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5)
               *   类是由native标识的,是调用了底层的c语言库
               *
               *    public final int getAndIncrement() {
               *         return unsafe.getAndAddInt(this, valueOffset, 1);
               *     }
               *
               *      public final int getAndAddInt(Object var1, long var2, int var4) {
               *         int var5;
               *         do {
               *             var5 = this.getIntVolatile(var1, var2);
               *         } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
               *
               *         return var5;
               *     }
               *
               * */
          }
      }
  2. CAS带来的ABA问题:
    1. package com.model.CAS;
      
      import java.util.concurrent.TimeUnit;
      import java.util.concurrent.atomic.AtomicInteger;
      import java.util.concurrent.atomic.AtomicStampedReference;
      
      /**
       * @Description:测试类
       * @Author: 张紫韩
       * @Crete 2021/6/10 14:13
       */
      public class CAS_ABA {
      
          /**
           * 当一个线程去获取一个值得时候 :获取到之后 A=1,在执行 compareAndSet(1,2)之前
           * 另一个线程也获取到了A=1 ,然后快速的执行 compareAndSet(1,2),和compareAndSet(2,1)
           * 这是 第一个线程在执行 compareAndSet(1,2)时 他拿到的A=1已经不是最新的值,已经被其他的线程修改了
           * */
      
          public static void main(String[] args) {
              //解决ABA问题,使用原子引用,
              AtomicInteger atomicInteger = new AtomicInteger(2021);
      
      //        AtomicStampedReference<Integer> atomicInteger = new AtomicStampedReference<>(2020, 1);
      
      
              //捣乱的线程
              new Thread(() ->{
                  System.out.println(Thread.currentThread().getName()+"获取到了"+atomicInteger.get());
                  try {
                      TimeUnit.SECONDS.sleep(2);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  System.out.print(Thread.currentThread().getName()+"修改了"+atomicInteger.compareAndSet(2021, 2022));
                  System.out.println(atomicInteger.get());
                  System.out.print(Thread.currentThread().getName()+"修改了"+atomicInteger.compareAndSet(2022, 2021));
                  System.out.println(atomicInteger.get());
                  System.out.println(Thread.currentThread().getName()+"修改完毕"+atomicInteger.get());
              },"B").start();
      
      
              new Thread(() ->{
                  System.out.println(Thread.currentThread().getName()+"获取到了"+atomicInteger.get());
                  try {
                      TimeUnit.SECONDS.sleep(3);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  //被骗的线程
                  System.out.println(Thread.currentThread().getName()+"修改了"+atomicInteger.compareAndSet(2021, 2022));
                  System.out.println(Thread.currentThread().getName()+"修改完毕"+atomicInteger.get());
              },"A").start();
      
          }
      }

       

    2. ABA问题解决,引用原子引用,
      1.     
      2.  

        CAS:方法中会有比较,比较时的出现的坑 ,不能超出包装类的 赋值范围

      3. package com.model.CAS;
        
        import java.util.concurrent.TimeUnit;
        import java.util.concurrent.atomic.AtomicInteger;
        import java.util.concurrent.atomic.AtomicStampedReference;
        
        /**
         * @Description:测试类
         * @Author: 张紫韩
         * @Crete 2021/6/10 14:13
         */
        public class CAS_ABA_JJ {
        
            /**
             * 当一个线程去获取一个值得时候 :获取到之后 A=1,在执行 compareAndSet(1,2)之前
             * 另一个线程也获取到了A=1 ,然后快速的执行 compareAndSet(1,2),和compareAndSet(2,1)
             * 这是 第一个线程在执行 compareAndSet(1,2)时 他拿到的A=1已经不是最新的值,已经被其他的线程修改了
             * */
        
            public static void main(String[] args) {
                //解决ABA问题,使用原子引用,
                //大坑:AtomicStampedReference包装类,注意,如果泛型是一个包装类,注意对象的引用问题
        //          integer使用了对象缓存机制,默认的范围是-128 - 127,推荐使用静态工厂模式的方法valueOf获取对象,而不是new ,因为valueOf使用了缓存,
        //        而new 一定会创建新的对象分配新的内存空间
                AtomicStampedReference<Integer> atomicInteger = new AtomicStampedReference<>(1, 1);
        
                //捣乱的线程
                new Thread(() ->{
        
                    int  stamp=atomicInteger.getStamp();  //获得的版本号
                    System.out.println(Thread.currentThread().getName()+"获得版本号"+stamp);
                    try {
                        TimeUnit.SECONDS.sleep(2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.print(Thread.currentThread().getName()+"修改了"+atomicInteger.compareAndSet(1, 2,atomicInteger.getStamp(),atomicInteger.getStamp()+1));
        
                    System.out.println(Thread.currentThread().getName()+"修改完毕版本号是:"+atomicInteger.getStamp());
                    System.out.print(Thread.currentThread().getName()+"修改了"+atomicInteger.compareAndSet(2, 1,atomicInteger.getStamp(),atomicInteger.getStamp()+1));
        
                    System.out.println(Thread.currentThread().getName()+"修改完毕版本号是:"+atomicInteger.getStamp());
                },"B").start();
        
        
                new Thread(() ->{
        
                    System.out.println(Thread.currentThread().getName()+"获取到版本号"+atomicInteger.getStamp());
                    try {
                        TimeUnit.SECONDS.sleep(3);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //被骗的线程
                    System.out.println(Thread.currentThread().getName()+"修改了"+atomicInteger.compareAndSet(2021, 2022,atomicInteger.getStamp(),atomicInteger.getStamp()+1));
        
                    System.out.println(Thread.currentThread().getName()+"修改完毕"+atomicInteger.getStamp());
                },"A").start();
        
            }
        }

         

         

         

posted @ 2021-06-10 16:13  张紫韩  阅读(74)  评论(0编辑  收藏  举报