Atomic 原子类详解
一、介绍
在 Java 1.5后引入了原子类操作类,这些操作的加入,让我们不用锁即可以实现原子操作,更加高效,简洁。
Atomic包下所有的原子类都只适用于单个元素,即只能保证一个基本数据类型、对象、或者数组的原子性。根据使用范围,可以将这些类分为四种类型,分别为原子更新基本类型、原子更新数组、原子更新引用、原子更新属性。
1、原子更新基本类型
原子更新基本类型包括 AtomicInteger、AtomicLong、AtomicBoolean 三种,分别对应3种基本数据类型,并且提供了几项基本操作:
1 // 获取当前值,然后自加,相当于i++ 2 getAndIncrement() 3 // 获取当前值,然后自减,相当于i-- 4 getAndDecrement() 5 // 自加1后并返回,相当于++i 6 incrementAndGet() 7 // 自减1后并返回,相当于--i 8 decrementAndGet() 9 // 获取当前值,并加上预期值 10 getAndAdd(int delta) 11 // 获取当前值,并设置新值 12 int getAndSet(int newValue)
2、原子更新引用类型
基本数据类型只能更新单个变量,如果要更新多个变量就需要用原子更新引用类型,引用类型的原子类包括AtomicReference、AtomicStampedReference、AtomicMarkableReference三个。
AtomicReference:引用原子类。
AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于解决原子的更新数据和数据的版本号,可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。(关于CAS及ABA问题后文详细分析)
AtomicMarkableReference:原子类更新带有标记的引用类型,该类将 boolean值与引用关联起来。
3、原子类更新数组
4、原子更新对象属性
如果直选哟更新某个对象中的某个字段,可以使用更新对象字段的原子类。包括三个类,AtomicIntegerFieldUpdater、AtomicLongFieldUpdater以及AtomicReferenceFieldUpdater。需要注意的是这些类的使用需要满足以下条件才可。
- 被操作的字段不能是static类型;
- 被操纵的字段不能是final类型;
- 被操作的字段必须是volatile修饰的;
- 属性必须对于当前的Updater所在区域是可见的。
二、CAS
![](https://img2022.cnblogs.com/blog/2637605/202203/2637605-20220309162804525-993626106.png)
因为CAS是通过检查值有没有发生改变来保证原子性的,假若一个变量V的值为A,线程1和线程2同时都读取到了这个变量的值A,此时线程1将V的值改为了B,然后又改回了A,期间线程2一直没有抢到CPU时间片。知道线程1将V的值改回A后线程2才得到执行。那么此时,线程2并不知道V的值曾经改变过。这个问题就被成为ABA问题。
ABA问题的解决其实也容易处理,即添加一个版本号,更次更新值同时也更新版本号即可。上文中提到的AtomicStampedReference就是用来解决ABA问题的。
CPU对 CAS 的支持:
在操作系统中CAS是一种系统原语,原语由多条指令组成,且原语的执行是连续不可中断的。因此CAS实际上是一条CPU的原子指令,虽然看上去CAS是一个先比较再交换的操作,但实际上这个过程是由CPU保证了原子操作。
三、原子类的实现 - UnSafe类 - 直接操作内存
Unsafe是一个神奇且鲜为人知的Java类,因为在平时开发中很少用到它。但是这个类中为我们提供了相当多的功能,它即可以让Java语言像C语言指针一样操作内存,同时还提供了CAS、内存屏障、线程调度、对象操作、数组操作等能力,如下图: