上文中介绍了原子化更新的必要性以及其基本实现原理,是使用Unsafe的API记性操作,UNsafe的API 主要是Native级别 的方法,因此,AtomicXXX 基本都是Unsafe类的封装,本篇文章笔者将从实际的类的源码中分析原子类操作的实现方式。
1、更新基本类型
使用原子类更新基本类型JUC 提供了三种类: AtomicBoolean,AtomicInteger以及AtomicLong。由于这三种基本类型的原理基本类似,笔者这里主要通过AtomicInteger 来分析对于基本类型的原子更新原理。
AtomicInteger 类主要有以下API用于操作:
// 1.以原子的方式将实例的值与给定的值#{delta} 相加并返回新值
int addAndGet(int delta);
// 2. 如果输入的值等于预期值#{expect},那么将值更新为 #{update}
boolean compareAndSet(int expect,int update){};
// 3. 以原子的方式设置实例的值为#{newValue} 并返回旧的值
int getAndSet(int newValue);
// 4. 以原子的方式自增1,并返回旧值
int getAndIncrement(();
比如下面的代码示例中就演示部分API 的使用
AtomicInteger integer = new AtomicInteger(1);
System.out.println(integer.getAndIncrement());
System.out.println(integer.get());
// 输出结果 1,2
那么 getAndIncrement()
是怎么实现的呢?
public final int getAndIncrement() {
return U.getAndAddInt(this, VALUE, 1);
}
@HotSpotIntrinsicCandidate
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = this.getIntVolatile(o, offset);
} while(!this.weakCompareAndSetInt(o, offset, v, v + delta));
return v;
}
经过浏览源码可知:
- 通过
v = this.getIntVolatile(o, offset);
获取到原来的值 - 通过
weakCompareAndSetInt
尝试设置将新增值加1 ,如果CAS 设置失败,那么返回第1步 - 如果设置成功,返回原来的v值
虽然Atomic只提供了三种操作类型: Boolean,Integer以及Long,其实其他的类型也是类似的,比如AtomicBoolean的实现就是将布尔类型转换为Integer,实际上你也可以自己实现AtomicChar 或者 AtomicShort类型。
2、更新数组类型
使用原子类更新数组主要有三种实现方式: AtomicIntegerArray, AtomicLongArray以及AtomicRefrenceArray。这里以AtomicIntegerArray 做一些简单的分析。
public static void main(String[] args) {
int[] intArray = new int[]{1,2,3,4};
AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(intArray);
atomicIntegerArray.getAndSet(0,0);
atomicIntegerArray.getAndSet(1,1);
atomicIntegerArray.getAndSet(2,2);
atomicIntegerArray.getAndSet(3,3);
}
> 需要注意的是,在构造 AtomicIntegerArray 的时候,其内部的数据是传递参数的一份拷贝,并非直接引用,所以在修改 atomicIntegerArray 的时候并不会对 intArray 有影响.
3、更新引用类型
原子更新基本类型,只能更新一个变量,比如一个整型Integer 或者布尔类型Boolean。如果需要原子更新多个引用变量,就会用到更新引用类型的 AtomicRefrence.
public static void main(String[] args) {
AtomicReference<User> atomicReference = new AtomicReference<>();
User oldUser = new User("张三", 12);
atomicReference.set(oldUser);
User newUser = new User("李四", 18);
atomicReference.compareAndSet(oldUser, newUser);
System.out.println("value = " + atomicReference.get());
}
@Data
@AllArgsConstructor
static class User {
String name;
Integer age;
}
4、原子更新字段类型
需要原子化更新某个类的某个字段的时候,则需要使用原子更新字段类。
- AtomicIntegerFieldUpdater 原子更新Integer的字段
- AtomicLongFieldUpdater 原子更新Long的字段
- AtomicStampedRefrencr 原子更新带版本号的引用类型
想要原子地更新某个字段,需要两个过程:
- 因为原子更新的字段都属于抽象类,所以在更新之前都必须使用
newUpdater
构建一个更新器 - 更新的字段类型必须为 public volatile 修饰
比如,我们想要更新User的年龄
public static void main(String[] args) {
// 构建更新器
AtomicIntegerFieldUpdater<User> updated =
AtomicIntegerFieldUpdater.newUpdater(User.class, "age");
User user = new User("张三", 12);
System.out.println(updated.incrementAndGet(user));
}
@Data
@AllArgsConstructor
static class User {
String name;
// 必须是public volatile 修饰
public volatile int age;
}