zoukankan      html  css  js  c++  java
  • CAS理解

    CAS:

    如果多个线程想对 count 变量进行自增操作,最先想到的是使用synchronized。

    初步方案:

    虽然随着Java版本更新,也对synchronized做了很多优化(偏向锁,轻量级锁),但是处理这种简单的累加操作,仍然显得“太重了”。多个线程使用synchronized,不就相当于让各个线程串行化了么?一个接一个的排队,加锁,处理数据,释放锁,下一个再进来。

    升级方案:

    Atomic包下的就是CAS的实现方式之一,他的底层使用的时unsafe类,提供了一个  do while 语句 compareAndSwapInt,期望是这样的值,需要先与内存的值比较是一样的时候,才执行加1的操作,把内存的值覆盖掉。
    syncchorized 属于悲观锁,CAS 属于乐观锁。

    两个线程进行CAS的操作如下:

    /**
     * @Author:daboluo
     * @Date: 2019/9/29 8:58
     * @Version 1.0
     *
     * AtomicInteger原子性验证
     */
    public class AtomicIntegerExample {
    
    
        private static int clientTotal = 5000;
    
        private static int threadTotal = 200;
    
        private static AtomicInteger count = new AtomicInteger();
    
    
        public static void main(String[] args) throws Exception{
            ExecutorService exec = Executors.newCachedThreadPool();
            // 对并发访问进行限制
            final Semaphore semaphore = new Semaphore(threadTotal);
            final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
            for (int i = 0; i < clientTotal; i++) {
                exec.execute(()->{
                    try {
                        semaphore.acquire();
                        add();
                        semaphore.release();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    countDownLatch.countDown();
    
                });
            }
            countDownLatch.await();
            exec.shutdown();
            System.out.println("count = " + count.get());
    
        }
    
        private static void add(){
            // 使用的时unsafe类,提供了一个  do while 语句   compareAndSwapInt
            // 期望时这样的值,与底层的值是一样的时候,才执行加1的操作,把底层的值覆盖掉
            count.incrementAndGet();
        }
    
    }

    缺点:

    1.CPU 开销大(使用LongAddr进行优化,竞争激烈的部分再进行分段)

    // Atomic一直在哪里循环的话,竞争激烈的话,修改失败概率更高,会进行多次的尝试,性能会受到影响
    // 对于普通类型的long 和double变量,jvm允许将64位的读操作或者写操作,拆成两个32位的操作
    // Longaddr 核心将atomic内部核心数据分离成一个数组,每个线程访问的时候,通过hash算法
    // 预测到其中一个数字进行计数,而最终的计数结果,则为这个数组的求和累加,热点数据value会被分离成多个单元的cell
    // 每个cell,维护各自独立的值,当前数据单元的值,由多个cell累计合成,这样热点就进行有效分离,提升并行度,
    // longAddr将单点的更新压力,分摊到多个节点上。在低并发的场景下,对base直接更新,可以跟atomic
    // 而高并发的场景下,通过发散提升列性能

    2.不能保证代码块的原子性,只能保证一个变量的原子性

    3.操作 ABA 问题(加版本号解决  AtomicStampedReference

     

  • 相关阅读:
    Linux下的MySQL主从同步
    人不能同时在两个地方做猪(Scrum Team)
    memcache安装
    Java开发中的Memcache原理及实现
    linux mv
    nginx
    idea 热部署
    vue watch
    vue入门
    基于vue-cli快速构建
  • 原文地址:https://www.cnblogs.com/Jemb/p/11616916.html
Copyright © 2011-2022 走看看