Java-多线程并发之原子操作类原理剖析

AtomicLong:主要进行一些自增自减和交换操作。原子性的前提下用的是非阻塞CAS的方法,比Synchronized效率高。但是高并发下仍会同时竞争一个原子变量。

 
 
但是java提供了一个更好用的LongAdder

LongAdder:

维护一个几只基值变量base和一个Cell数组。Cell数组可以看作是AtomicLong的改进,用来解决共享问题。

LongAdder相当于将Atomic中的原子变量分裂成幂次方个,线程的压力就少了很多。而且LongAdder是惰性加载,延迟初始化,初始化的时候Cell数组是null,累加操作数据都放在基值变量base上。初始化时候,cell的变量为2,Cell数组的变量实体是Cell.

对于多个原子操作进行字节填充是浪费的,因为原子操作都是无规律的散放在内存中。但是这里的原子性数组是内存地址连续的,刚才说的散放的是不同的原子性操作。

所以原子数组内多个元素经常可以共享到一个缓存行,一般用注解的方式@sun.misc.Contended.性能会有所提升。

通过源代码可以看出,通过cellBusy对数组的初始化和扩容操作进行自旋锁操作,在0/1切换。在初始化或开始扩容阶段要设置为1,初始化完成或结束后设置为0,这样可以避免其他线程的打扰。

扩容的条件是满足

1.cell元素个数小于CPU数目
2.多线程访问cell的同一个元素导致CAS失败
 
关于CAS失败,LongAdder的做法是重新计算一个probe值。Probe值的作用有点儿类似于hashcode.通过随机的值来确定当前的线程应该存放到哪个cell中去。
 
 
 
LongAdder是LongAccumulator的一个特例。
LongAccumulator的功能更加强大,可以让用户去自定义累加规则。
 
posted @ 2021-04-06 19:23  NobodyHero  阅读(80)  评论(0编辑  收藏  举报