Juc16_LongAdder引入、原理、Striped64、分散热点思想、深度解析LongAdder源码、LongAdder和AtomicLong区别(一)

简介: ①. LongAdder的引入、原理、能否代替AtomicLong②. Striped64

①. LongAdder的引入、原理、能否代替AtomicLong


①. 我们知道,AtomicLong是利用底层的CAS操作来提供并发性的,比如addAndGet方法:


下面方法调用了Unsafe类的getAndAddLong方法,该方法是一个native方法,它的逻辑是采用自旋的方式不断更新目标值,直到更新成功。(也即乐观锁的实现模式)


在并发量比较低的情况下,线程冲突的概率比较小,自旋的次数不会很多。但是,高并发情况下,N个线程同时进行自旋操作,N-1个线程失败,导致CPU打满场景,此时AtomicLong的自旋会成为瓶颈


这就是LongAdder引入的初衷------解决高并发环境下AtomictLong的自旋瓶颈问题


    public final long addAndGet(long delta) {
        return unsafe.getAndAddLong(this, valueOffset, delta) + delta;
    }


②. LongAdder在无竞争的情况,跟AtomicLong一样,对同一个base进行操作,当出现竞争关系时则采用化整为零的做法,从空间换时间,用一个数组cells,将一个value拆分进这个数组cells。多个线程需要同时对value进行操作时候,可以对线程id进行hash得到hash值,再根据hash值映射到这个数组cells的某个下标,再对该下标所对应的值进行自增操作。当所有线程操作完毕,将数组cells的所有值和无竞争值base都加起来作为最终结果(分散热点)


微信图片_20220108132409.png


③. LongAdder能否替代AtomicLong?


从下面的图中可以看到,LongAdder的API和AtomicLong的API还是有比较大的差异,而且AtomicLong提供的功能更丰富,尤其是addAndGet、decrementAndGet、compareAndSet这些方法。addAndGet、decrementAndGet除了单纯的做自增自减外,还可以立即获取增减后的值,而LongAdder则需要做同步控制才能精确获取增减后的值。如果业务需求需要精确的控制计数,则使用AtomicLong比较合适;


低并发、一般的业务尝尽下AtomicLong(数据准确)是足够了,如果并发量很多,存在大量写多读少的情况,那LongAdder(数据最终一致性,不保证强一致性)可能更合适


微信图片_20220108132430.png


微信图片_20220108132500.png


image.png


②. Striped64


  • ①. Striped64有几个比较重要的成员函数


  //CPU数量,即Cells数组的最大长度
  static final int NCPU = Runtime.getRuntime().availableProcessors();
  //存放Cell的hash表,大小为2的幂
  //这里的Cell是Striped64的内部类
  transient volatile Cell[] cells;
  /*
  1.在开始没有竞争的情况下,将累加值累加到base;
  2.在cells初始化的过程中,cells处于不可用的状态,这时候也会尝试将通过cas操作值累加到base
  */
  transient volatile long base;
  /*
  cellsBusy,它有两个值0或1,它的作用是当要修改cells数组时加锁,
  防止多线程同时修改cells数组(也称cells表),0为无锁,1位加锁,加锁的状况有三种:
  (1). cells数组初始化的时候;
    (2). cells数组扩容的时候;
    (3).如果cells数组中某个元素为null,给这个位置创建新的Cell对象的时候;
  */
  transient volatile int cellsBusy; 


②. Striped64中一些变量或者方法的定义


base: 类似于AtomicLong中全局的value值。再没有竞争情况下数据直接累加到base上,或者cells扩容时,也需要将数据写入到base上


collide:表示扩容意向,false一定不会扩容,true可能会扩容


cellsBusy:初始化cells或者扩容cells需要获取锁,0表示无锁状态,1表示其他线程已经持有了锁


casCellsBusy:通过CAS操作修改cellsBusy的值,CAS成功代表获取锁,返回true


NCPU:当前计算机CPU数量,Cell数组扩容时会使用到


getProbe( ):获取当前线程的hash值


advanceProbe( ):重置当前线程的hash值


③. LongAdder是Striped64的子类、架构图


微信图片_20220108132546.png


微信图片_20220108132609.png


④. Cell:是java.util.concurrent.atomic下Striped64下的一个内部类


微信图片_20220108132616.png




相关文章
|
3天前
|
Java
并发编程之线程池的底层原理的详细解析
并发编程之线程池的底层原理的详细解析
14 0
|
2天前
|
缓存 JavaScript 前端开发
|
3天前
|
SQL 分布式计算 资源调度
一文解析 ODPS SQL 任务优化方法原理
本文重点尝试从ODPS SQL的逻辑执行计划和Logview中的执行计划出发,分析日常数据研发过程中各种优化方法背后的原理,覆盖了部分调优方法的分析,从知道怎么优化,到为什么这样优化,以及还能怎样优化。
|
3天前
|
JSON Java Maven
Javaweb之SpringBootWeb案例之 SpringBoot原理的详细解析
Javaweb之SpringBootWeb案例之 SpringBoot原理的详细解析
8 0
Javaweb之SpringBootWeb案例之 SpringBoot原理的详细解析
|
3天前
|
前端开发 JavaScript 编译器
深入解析JavaScript中的异步编程:Promises与async/await的使用与原理
【4月更文挑战第22天】本文深入解析JavaScript异步编程,重点讨论Promises和async/await。Promises用于管理异步操作,有pending、fulfilled和rejected三种状态。通过.then()和.catch()处理结果,但可能导致回调地狱。async/await是ES2017的语法糖,使异步编程更直观,类似同步代码,通过事件循环和微任务队列实现。两者各有优势,适用于不同场景,能有效提升代码可读性和维护性。
|
8天前
yolo-world 源码解析(六)(2)
yolo-world 源码解析(六)
18 0
|
8天前
yolo-world 源码解析(六)(1)
yolo-world 源码解析(六)
12 0
|
9天前
yolo-world 源码解析(五)(4)
yolo-world 源码解析(五)
22 0
|
9天前
yolo-world 源码解析(五)(1)
yolo-world 源码解析(五)
31 0
|
9天前
yolo-world 源码解析(二)(2)
yolo-world 源码解析(二)
21 0

推荐镜像

更多