Java8原子类的增强

无锁的原子类操作使用系统的CAS指令,Java8中引入了LongAdder类,来进一步提高性能。

AtomicInteger的基本实现原理是,在一个死循环内,不断尝试修改目标值,直至修改成功。如果竞争不激烈,则修改成功率就比较高,否则失败率就很高。当大量修改失败时,这些原子操作就会进行多次死循环尝试,导致性能受到影响。

当竞争激烈时可以使用热点分离,将竞争的数据进行分解进而提升性能。虽然CAS操作中没有锁,但减小锁粒度的思想依然可以使用。一种方案是仿造ConcurrentHashMap将热点数据分离,比如将AtomicInteger的内部核心数据value分离成一个数组,每个线程访问时,通过哈希算法映射到其中一个数字进行计数,最终的计数结果为此数组的求和值。LongAdder类正是使用了这种思想来实现的。

在实际操作中LongAdder并不会一开始就用数组进行处理,而是将所有数据都记录在一个称为base的变量中。如果在多线程下修改base没有冲突,则没有必要扩展为cell数组。一旦base修改发生冲突,就会初始化cell数组,使用新的策略。如果cell数组更新后,发现在某个cell上更新依然冲突,则系统会尝试创建新的cell或将cell的数量加倍,以降低冲突的可能性。

increment()实现

increment()方法会将LongAdder自增1,开始时cells=null,数据会向base增加,如果对base的操作冲突,则会设置冲突标记uncontended=true。如果cells不可用或当前线程对应的cell=null,则进入longAccumulate()方法。longAccumulate()的功能是根据需要创建新的cell或对cells扩容,以减少冲突。

以下是LongAdder、原子类和同步锁的性能测试:

运行结果

LongAdder的另一个优化是避免了伪共享,它使用注解方式实现(@sun.misc.Contended),JVM会自动为Cell解决伪共享问题。我们也可以使用sun.misc.Contended来解决伪共享问题(可以声明在类上),但需要设置JVM参数-XX:-RestrictContended,否则该注释将被忽略。

LongAdder中cell定义:

cell定义

LongAdder的增强版:LongAccumulator

LongAccumulator同样继承于Striped64,它的内部优化方式和LongAdder一样,都是将一个long型值进行分割,存储在不同变量中,防止多线程竞争。LongAccumulator是LongAdder的扩展,LongAdder只是每次对给定的整数执行一次加法,而LongAccumulator可以实现任意函数操作。

构造方法
LongBinaryOperator接口定义

构造方法的第一个参数accumulatorFunction是需要执行的二元函数,第二个参数是初始值。

accumulate()定义
使用LongAccumulator得到多线程下最大的数字


--参考文献《实战Java高并发程序设计》

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 一、简介  在之前的《ConcurrentHashMap深入剖析(JDK8)》文章中,我们看到了CounterCe...
    SunnyMore阅读 5,830评论 1 6
  • 基本原理和思想  Java有很多并发控制机制,比如说以AQS为基础的锁或者以CAS为原理的自旋锁。一般来说,CAS...
    Java耕耘者阅读 4,881评论 0 0
  • 背景介绍 说到并发编程的问题,大多数人第一反应想到的就是大多数举线程安全例子时出现的一段代码: 然后很自然地想到,...
    XHLeee阅读 5,903评论 0 1
  • 初夏黄昏时分 呆呆的趴在窗前 看着天空 淡蓝色的肖像 时过满年 信笺不再 那些笔下的曾经 墨色香味 一言一语...
    山未素阅读 1,378评论 0 4
  • Dove sei...cara mia... 记得一年前自己还会在咖啡吧里面享受着已经让人慵懒的阳光... 喝一杯...
    钩吻屋阅读 1,306评论 0 0