zoukankan      html  css  js  c++  java
  • CAS

    CAS概念:

      CAS号称是无锁优化,或者叫自旋。

    我们先通过一个简单的例子来说明为什么要使用CAS。

    public class T {
    
        private int count = 10;
    
        public void m() {
                count--;
                System.out.println(Thread.currentThread().getName() + " count = " + count);
        }
    
        public static void main(String[] args) {
            T t = new T();
            for (int i = 0; i < 10 ; i++) {
                new Thread(()->t.m()).start();
            }
        }
    
    }
    View Code

    这段代码的执行结果如下:

     可以看到结果并不是我们想要的,我们可以通过加锁的方式去实现:

    public class T {
    
        private int count = 10;
    
        public synchronized void m() {
                count--;
                System.out.println(Thread.currentThread().getName() + " count = " + count);
        }
    
        public static void main(String[] args) {
            T t = new T();
            for (int i = 0; i < 10 ; i++) {
                new Thread(()->t.m()).start();
            }
        }
    
    }
    View Code

    结果如下:

     虽然我们实现了同步,但是加锁对系统资源的消耗是非常大的,所以java为我们提供了一些通过CAS实现原子变量类,

    比如我们来看如下代码:

    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.atomic.AtomicInteger;
    
    public class T2 {
    
        private int count = 0;
    
        public void m() {
            count++;
        }
    
        public static void test(){
            T2 t = new T2();
            List<Thread> threadList = new ArrayList<>();
    
            for (int i = 0; i < 55000 ; i++) {
                threadList.add(new Thread(()->t.m()));
            }
            threadList.forEach(thread -> thread.start());
    
            threadList.forEach(thread -> {
                try {
                    thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            System.out.println(t.count);
        }
    
        public static void main(String[] args) {
            for (int i = 0; i <10 ; i++) {
                test();
            }
        }
    
    }
    View Code

    执行结果如下:

     可以看到,我们通过多线程去操作一个共享变量,得到得结果并不是准确的,也就是说存在线程安全问题。当然我们可以用synchronized关键字来同步,但是比较笨重。

    这里我们可以使用Atomic包下的AtomicInteger来实现同步,具体代码如下:

    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.atomic.AtomicInteger;
    
    public class T1 {
    
        private AtomicInteger count = new AtomicInteger(0);
    
        public void m() {
            count.incrementAndGet();
        }
    
        public static void  test(){
            T1 t = new T1();
            List<Thread> threadList = new ArrayList<>();
    
            for (int i = 0; i < 55000 ; i++) {
                threadList.add(new Thread(()->t.m()));
            }
    
            threadList.forEach(thread -> thread.start());
    
            threadList.forEach(thread -> {
                try {
                    thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            System.out.println(t.count);
        }
    
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                test();
            }
        }
    
    }
    View Code

    执行结果如下:

     可以看到我们没有加锁,但是同样达到了同步的目的。AtomicInteger的底层就是CAS实现的,那么CAS究竟是怎么实现的呢?

    CAS好比一个代理人(中间人),共享同一变量V的线程就是他的客户,当客户需要更新变量V的值时,他们需要请求代理人代为

    修改,为此,客户要告诉代理人其看到的共享变量的当前值A以及期望值B。CAS作为代理人,相当于如下伪代码所示的函数:

    boolean compareAndSwap(Variable V,Object A,Object B){
        //如果线程看到的值和当前值A相等,则说明共享变量V没有被其他线程改变
        if(A==V.get()){
            V.set(B);//更新变量值
            return true;//更新成功
        }
        return false;//更新失败,继续请求代理人代为修改
      }

    该操作的原子性由处理器保障,所以该操作不存在线程安全问题。

    java.util.concurrent.atomic下的类的底层都是通过CAS来保障线程安全的,这里就不一一举例了。很多小伙伴会发现我们在实际

    开发中,其实很少用到这些类,基本上大家都是用synchronized加锁。但是在超高并发的情况下,我们就要慎重选择了。但是我们

    一定不要武断的认为synchronized效率一定比CAS低,现在synchronized有了锁升级的概念,在不是超高并发的情况下,真的并不

    一定效率低于CAS。

     ABA问题:

      ABA问题其实很简单,我们回过头去看CAS的实现,当当前线程期望的值和共享变量相等的时候,我们就判定共享变量没有被修改,

    但是真的是这样吗?其实不是的,如果在这期间共享变量A被一个线程修改为B,然后又被另一个线程修改为A,其实以及发生了改变,只是

    又被改回了原来的值,当前线程无法得知而已。这就是ABA问题。

      那么如何规避ABA问题呢,也很简单,只需要在更新共享变量的的时候引入一个修订号,每次都增加1。例如A-B-A改为[A,0]-[A,1]-[A-1]。

    这样我们就能准确的判断共享变量是否被其他线程修改过。AtomicStampedReference类就是基于这种思想产生的。

  • 相关阅读:
    让keep-alive不缓存
    vue tab echart始终显示100px解决办法
    加载图片失败显示默认图片
    element 文件上传传参数
    vue-cli axios 拦截器设置
    制作字体图标制作
    20道Java面试必考题
    Integer源码解析
    阿里P9架构师讲解从单机至亿级流量大型网站系统架构的演进过程
    代理详解
  • 原文地址:https://www.cnblogs.com/liu-yi/p/13499736.html
Copyright © 2011-2022 走看看