本文最后更新于:2024年4月22日 下午
理解atomic原子类的基本类型原子类、数组类型原子类、引用类型原子类、对象的属性修改原子类的概要思想与案例分析
18罗汉=12原子类+4增强类+Striped64类+Number类
基本类型原子类
AtomicInteger
扩展知识CountDownLatch
它是一个同步工具类,允许一个或多个线程一直等待,直到其他线程运行完成后再执行。
使用场景:
AtomicBoolean
原子更新布尔类型,用法类似
AtomicLong
原子更新长整型,用法类似
原子更新数组
AtomicIntegerArray
原子更新整型数组里的元素。
AtomicLongArray
原子更新长整型数组里的元素。
AtomicReferenceArray
原子更新引用类型数组里的元素。
引用类型原子类
上一章节讲到的ABA问题,这里就使用AtomicStampedReference和AtomicMarkableReference解决
AtomicReference
原子更新引用类型,通过CAS原理调用compareAndSet
AtomicStampedReference
原子更新引用类型, 内部使用Pair来存储元素值及其版本号,携带版本号
的引用类型原子类,解决修改过几次
AtomicMarkableReference
原子更新带有标记位的引用类型,原子更新带有标记位
的引用类型对象,解决是否修改过
对象的属性修改原子类
以一种线程安全的方式操作非线程安全对象内的某些字段
使用要求(重点)
1、更新的对象属性必须使用 public volatile
修饰符
2、因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须
使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性。
AtomicIntegerFieldUpdater
AtomicLongFieldUpdater
AtomicReferenceFieldUpdater
原子操作增强类
- DoubleAccumulator
- DoubleAdder
- LongAccumulator
- LongAdder
Adder和Accumulator区别:Adder只能用来计算加法,且从零开始计算;Accumulator提供了自定义的函数操作
增强类(Adder、Accumulator)和Atomic的区别?
具体:LongAdder 与 AtomicLong有什么区别?
概述
AtomicLong
是作者Doug Lea
在jdk1.5
版本发布于java.util.concurrent.atomic
并发包下的类。
而LongAdder
是道格·利(Doug Lea的中文名)
在java8
中发布的类。
原理剖析
AtomicLong底层是通过CAS
实现的,采用volatile+Unsafe类来实现。
缺点:
1、在多线程竞争不激烈的情况下,这样做是合适的。但是如果线程竞争激烈,会造成大量线程在原地打转、不停尝试去修改值,但是老是发现值被修改了,于是继续自旋。 这样浪费了大量的CPU资源
。
2、volatile,线程修改了临界资源后,需要刷新到其他线程,也会浪费资源
注意:Adder的实现sum求和后还有计算线程修改结果的话,最后结果不够准确
Striped64类
LongAdder是Striped64的子类
核心参数
Striped64有几个比较重要的成员函数
static final int NCPU = Runtime.getRuntime().availableProcessors();
CPU数量,即cells数组的最大长度
transient volatile Cell[] cells;
cells数组,为2的幂,2,4,8,16…..,方便以后位运算
transient volatile long base;
基础value值,当并发较低时,只累加该值主要用于没有竞争的情况,通过CAS更新。
transient volatile int cellsBusy;
创建或者扩容Cells数组时使用的自旋锁变量调整单元格大小(扩容),创建单元格时使用的锁。
LongAdder实现原理:
LongAdder的基本思路就是分散热点,将value值分散到一个Cell数组中,不同线程会命中到数组的不同槽中,各个线程只对自己槽中的那个值进行CAS操作,这样热点就被分散了,冲突的概率就小很多。如果要获取真正的long值,只要将各个槽中的变量值累加返回。
sum()会将所有Cell数组中的value和base累加作为返回值,核心的思想就是将之前AtomicLong一个value的更新压力分散到多个value中去
为啥在并发情况下longAdder的sum的值不精确?