前言
Java线程中的java.util.concurrent.atomic包里面都是类都是针对多线程下的原子变量,有包括AtomicInteger, AtomicBoolean等等多种变量的原子化实现。
本次我们将会解读AtomicInteger的源码,对变量的原子化思路进行一个理解。这也会对理解现实场景中,多线程程序原子化使用某个资源也有更好的理解。我们抽取几个主要的方法进行解读。
画个UML
AtomicInteger主要实现了Number接口,这个接口提供的方法都是将原子变量值转换为其他类型值的接口。
而主要的原子化特性则是通过持有jdk.internal.misc.Unsafe对象实现Proxy模式进行实现。
初始化
两个静态变量十分清晰:
U
是调用了jdk.internal.misc.Unsafe对象,协助后面的原子性更新特性。
VALUE
则是使用了objectFieldOffset获取了对象在JVM内存中的地址。该方法的具体实现则是一个native方法,通过C或C++对JVM内部进行操作。暂时不作细究。
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long VALUE = U.objectFieldOffset(AtomicInteger.class, "value");
private volatile int value;
构造函数
AtomicInteger(int)
将传入int值,写入为成员变量,令对象持有该值。
主要API及源码解读
int get()
比较直观的实现,直接返回被声明为volatile的value。
void set(int)
比较直观的实现,将输入值赋值到为volatile的value。
int getAndIncrement()
有效的逻辑,在调用Unsafe对象的下面两个方法:
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {// 循环
// getIntVolatile是一个native方法:
// 以内存地址,及Object对象确定获取内存中的volatile值
v = getIntVolatile(o, offset);
} while (!weakCompareAndSetInt(o, offset, v, v + delta));
//调用下方的weakCompareAndSetInt方法,直到更新成功则退出循环
return v;
}
public final boolean weakCompareAndSetInt(Object o, long offset,
int expected,
int x) {
// 调用native方法compareAndSetInt
// 以对象类型o,内存地址offset,期望值expected,输入值x
// 按照对象类型及内存地址获得期待值,对比后更新为输入值。
return compareAndSetInt(o, offset, expected, x);
}
int getAndDecrement()
类似于getAndIncreement,调用Unsafe,对内存中数值进行操作。
小结及延伸思考
通过回顾针对AtomiInteger的几个基本方法原子化操作的解读,我们可以理解为原子操作的几个要点:
- 采用volatile类型的特性,直接读取内存中的值,忽略掉VM中的值。
- 采用对比的方法,对比对象类型,内存地址,原有值等维度,核对后进行赋值。(具体要检查native方法的代码)
- Atomic类忽略了值的次序性,尽可能以值的唯一性保证其原子性。
对于数据库记录的原子性的解决方案,也有类似的解决方案,多线程占用某个数据库资源更新时,我们也可以先做读取,核对值或版本号后再作更新。
对于数据库相关的原子性保证更新,我们下一篇再聊聊。