打算把Concurrent包下面的一些类整理一下。今天先从最简单的开始,Atomic原子类。
先看一下atomic包下的类
一共12个,共分四类:基本类型、数组、引用、属性。我们就按这四个分类,介绍一下它们的用途以及实现。
基本类型
包含上图红框里面的三个类。由于三个类提供的方法基本一致,下面以AtomicInterger为例。
AtomicInteger的常用方法如下:
实现逻辑都差不多,取当前值current,然后计算更改后的值。利用CAS思想,比较current和当前数值是否相等,如果相等,则更新,如果不相等,则重复上述操作。下面借助源码分析:
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
for (;;) {
int current = get(); //获取当前值
int next = current + 1; //执行加1操作
if (compareAndSet(current, next))
return current; //返回当前值
}
}
可以看出,通过compareAndSet
方法,保证原子操作。
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
调用Unsafe类的compareAndSwapInt方法。Unsafe类中方法为native方法,具体未看,原理就是利用CAS。
java有8个基本类型,而这里只有3个,那么其他5个如果也想原子操作呢?其实可以参考AtomicBoolean的实现,它先将boolean类型值转换成int。
数组
原子数组类型参见图中绿色框中的3个类。其底层实现逻辑和原子基本类型一致,这里我们看看如何使用数组类型。
static int[] value = new int[]{1,2};
static AtomicIntegerArray ai = new AtomicIntegerArray(value);
public static void main(String[] args){
ai.getAndSet(0, 3);
System.out.println(value[0]);
System.out.println(ai.get(0));
}
思考一下输出,再看结果:
1
3
是不是和你预想的不一样?传递的数组,为什么没有更改原始数组呢?AtomicIntegerArray会复制传递过来的数组,所以,AtomicIntegerArray修改内部数组元素时,不会影响传入的数组。
原子引用类型
3个原子引用类型,参加图中黄色框中的类。
- AtomicReference:原子引用类型
- AtomicStampedReference:原子带有版本号的引用类型
- AtomicMarkableReference:原子带有标记位的引用类型
原子字段类
参见上图未框住的3个类。
需要注意的是,因为原子字段类为抽象类,每次使用时,通过静态方法newUpdater()创建一个更新器,并且设置需要更新的类和属性。同时,更新类的字段必须用volatile修饰。
参考:《Java并发编程的艺术》