线程安全之原子性--Atomic包-Part-2

AtomicLong与AtomicInteger

根据part-1中代码,其实使用AtomicLong代替AtomicInteger,而其他不用替换是完全可以的。同样也是线程安全的。

AtomicLong与LongAdder

但是重点是JDK 1.8中新增的LongAdder类,它与AtomicLong在上节的测试中有相同的效果(线程安全),只需稍微换成各自对应的方法即可。

那两者的区别是什么?

肯定的说,新增的该类一定或多或少比AtomicLong类及AtomicInteger类有优点的,那么是什么?
浅析如下:

优点:
  1. 由于AtomicLong的底层是CAS原理,即通过死循环不断地比较当前值与主内存的值,那么当线程并发量较少时其比较成功的概率是高的,但放并发量很大时,比较成功概率就会很低,越低则死循环持续的时间越久,占用的系统资源越大,系统运行的效率越低。

  2. 对于基础数据类型的long或double时,JVM会允许将64位的读操作或写操作拆分为两个32位的操作。同理,LongAdder也是采用相似的思想。

    LongAdder的实现思想为:热点数据分离:将AtomicLong的核心数据value分离为一个数组,每个线程访问时通过hash等算法预测到其中的一个数字进行计数,最终的计数结果为该数组各部分的求和。热点数据value会被分为多个部分的shell,每个shell独自维护各自的值,而当前对象的实际值为各部分shell的累加和。保证热点数据的有效分离,提高并行度。LongAdder类实际是在AtomicLong的基础上进行了优化:在低并发时不做分离,同AtomicLong是对base的直接CAS更新,但是高并发时将单点的压力分摊到各部分的shell上以提高性能。

缺点:

该类将数据分类后,再次进行各部分shell的数据进行统计时,如果此时存在并发更新,可能会导致统计的数据出现误差。

实际使用中,在处理高并发情况的时候要优先使用LongAdder类。当线程竞争低、全局唯一的准确是数值类似序列号生成等情况优先使用AtomicXXX类

知识拓展补充

下表为JAVA的8种基本数据类型所占位数及初始值:
基础数据类型 | 所占位数 | 初始值
-|-|-
byte字节型| 1字节(8bit) | 0
char字符型 | 2字节(16bit) | 空格
short短整型 | 2字节(16bit) | 0
int整型 | 4字节(32bit) | 0
long长整型 | 8字节(64Bit) | 0L
float单精度浮点型 | 4字节(32bit) | 0.0f
double双精度浮点型 | 8字节(64bit) | 0.0d
boolean | java未明确指出的大小(可能1bit、1byte、4byte) | false

SupriseMF wechat
欢迎关注微信订阅号【星球码】,分享学习编程奇淫巧技~
喜欢就支持我呀(*^∇^*)~

热评文章