一文详解 Java 的几把 JVM 级锁( 六 )

LongAdder

在高并发的情况下 , 我们对一个 Integer 类型的整数直接进行 i++ 的时候 , 无法保证操作的原子性 , 会出现线程安全的问题 。 为此我们会用 juc 下的 AtomicInteger , 它是一个提供原子操作的 Interger 类 , 内部也是通过 CAS 实现线程安全的 。 但当大量线程同时去访问时 , 就会因为大量线程执行 CAS 操作失败而进行空旋转 , 导致 CPU 资源消耗过多 , 而且执行效率也不高 。 Doug Lea 大神应该也不满意 , 于是在 JDK1.8 中对 CAS 进行了优化 , 提供了 LongAdder , 它是基于了 CAS 分段锁的思想实现的 。 线程去读写一个 LongAdder 类型的变量时 , 流程如下: LongAdder 也是基于 Unsafe 提供的 CAS 操作 +valitale 去实现的 。 在 LongAdder 的父类 Striped64 中维护着一个 base 变量和一个 cell 数组 , 当多个线程操作一个变量的时候 , 先会在这个 base 变量上进行 cas 操作 , 当它发现线程增多的时候 , 就会使用 cell 数组 。 比如当 base 将要更新的时候发现线程增多(也就是调用 casBase 方法更新 base 值失败) , 那么它会自动使用 cell 数组 , 每一个线程对应于一个 cell , 在每一个线程中对该 cell 进行 cas 操作 , 这样就可以将单一 value 的更新压力分担到多个 value 中去 , 降低单个 value 的 “热度” , 同时也减少了大量线程的空转 , 提高并发效率 , 分散并发压力 。 这种分段锁需要额外维护一个内存空间 cells , 不过在高并发场景下 , 这点成本几乎可以忽略 。 分段锁是一种优秀的优化思想 , juc 中提供的的 ConcurrentHashMap 也是基于分段锁保证读写操作的线程安全 。 作者信息: 夏杰 , 花名楚昭 , 现就职于阿里巴巴企业智能事业部 BUCACLSSO 团队 , 面向阿里巴巴经济体提供人员账号的权限管控、应用数据安全访问治理 , 并通过现有的技术沉淀与领域模型 , 致力于打造 To B、To G 领域的应用信息化架构的基础设施 SAAS 产品 MOZI 。

推荐阅读