Atomic原子系列基本使用
1、初识atomic原子系列
在 jvm 单进程中,往往会涉及到在多线程下一些关于数据的增加的问题,如典型的数据类加问题,通常情况是可
以直接采用悲观锁 synchronized
关键字来实现的,但是由于悲观锁需要涉及到用户态到内核态直接的切换,会
严重的影响该场景下的性能问题,因此在后面,通过 cas
底层实现的 atomic 算法就此而生。
在 java.util.concurrent 下面有一个 atomic 的原子包,里面有着多个关于 atomic 的原子实现类,atomic 主要能
实现的数据类型可以归纳为五种:
- 基本数据类型
- 引用数据类型
- 数组数据类型
- 对象属性修改器
- 原子类型累加器
2、CAS的方式实现atomic原子类的底层
cas 的底层实现,主要是通过内部自旋加调用硬件层面的指令来实现数据的原子性,通过 cmpxchg
指令来实现比
较和交换的操作,从而实现总线加锁,并通过一个 #lock
前缀指令来实现 storeLoad
内存屏障的功能,从而解
决在多线程中共享变量的可见性、原子性和有序性。
在 atomic 中,其底层实现就是通过 cas 的原理来实现的,由于 cas 的缺点之一就是只能操作一个变量,atomic
原子包的主要思想就是对单个变量进行操作,因此 atomic 采用 cas 作为底层实现最好不过,并且可以减少用户态
到内核态之间的切换,在一定的数据范围内,其效率是远远高于这个 synchronized 这些锁的。
如初始化一个 AtomicInteger
原子类,如下:
AtomicInteger atomicInteger = new AtomicInteger(0);
接下来对这个类进行一个自增的操作,就是调用其 incrementAndGet
方法:
// 先自增,再将值放回
atomicInteger.incrementAndGet()
其底层的实现如下,会通过一个unsafe类的一个实例,unsafe类就是介于java类和硬件层面打交道的类:
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
随后再查看这个unsafe类调用的这个 getAndAddInt 方法,很明显,这个方法就是比较和交换的底层实现:
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
//var5 工作内存中的初始值,就是旧值
var5 = this.getIntVolatile(var1, var2);
//var1 当前值所占的字节数 var2 offset偏移量
//var5 + var4 累加完的值
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
//最终返回的是工作内存的初始值。因此需要在外面再+1
return var5;
}
而最终调用这个 native 本地方法栈中的 compareAndSwapInt 方法,再去调用底层的硬件实现比较和交换的操
作:
public final native boolean compareAndSwapInt(Object var1,long var2,int var4,int var5);
3、五种数据类型的基本使用
3.1 基本数据类型
基本数据类型主要有:AtomicInteger
、AtomicBoolean
、AtomicLong
这三种,以AtomicInteger来举例,其
用法主要如下:
// 初始化AtomicInteger对象
AtomicInteger atomicInteger = new AtomicInteger(0);
在这个 AtomicInteger 类中,里面可以使用的方法主要如下图,如一些getAndAdd,addAndGet,
getAndIncrement,getAndDecrement,incrementAndGet,deCrementAndGet等等。都会涉及到是先自增在
获取值还是先获取值再自增的操作。
相关的AtomicInteger类的api的使用命令如下:
public class AtomicIntegerTest {
public static void main(String[] args) {
//初始化 AtomicInteger 对象
AtomicInteger atomicInteger = new AtomicInteg