概述
在上篇博客中,我简单介绍了无锁同步 CAS 如何使用以及部分它的特性。本篇我打算整理一下 CAS 的实现原理即 Unsafe 类相关的知识。
Unsafe
本篇博客分以下几个模块展开:
- Unsafe 类简单介绍
- CAS 更新基础类型原理
- CAS 更新对象引用原理
- CAS 更新数组类型原理
- CAS 更新对象属性原理
- Unsafe 类方法总结
- Unsafe 类示例
1、Unsafe 类简单介绍
Unsafe 类处于包 sun.misc 下,该包由 sun 公司内部实现,不属于 J2EE 开发规范。
java 代码中任何 CAS 操作最终都是通过调用 Unsafe 类中的 native 方式实现,也就是说:Unsafe 通过调用操作系统底层资源实现任务。
从名称就可以看出,Unsafe 类是不安全的。它可以像C语言指针那样直接操作内存。一般我们不建议直接使用 Unsafe 类处理任务。
2、CAS 更新基础类型原理
上篇博客我们提到 CAS 可以更新基础类型数据,这里我们就拿 AtomicInteger 类的源码进行分析:
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) {
throw new Error(ex); }
}
private volatile int value;
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
通过上面的源码,我们可以看出:AtomicInteger 类的 CAS 方法最终还是调用了 Unsafe 类的方法。Unsafe 类方法的源码如下所示:
public native long objectFieldOffset(Field var1);
@CallerSensitive
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
首先通过 Reflection.getCallerClass() 可以表明,调用此方法必须包含以下权限:
- 由 bootstrap class loader 加载的类可以调用
- 由 extension class loader 加载的类可以调用
而我们用户编写的类都是通过 application class loader 加载的,也就是说用户编写的代码不能通过这个静态方法直接获取对象实例,并且 Unsafe 类本身也没有提供公开的构造方法。
这样做的原因也非常明显:防止用户直接使用 Unsafe 类。然而事实上,只要愿意的话,同样可以通过反射创建该对象实例,相应出现的风险也需要程序员自己承担。
下面我们再看 AtomicInteger 对象调用 Unsafe 类方法时所传递的参数:
- this:对象本身
- valueOffset:AtomicInteger 对象 value 属性偏移地址
- expect:期望值
- update:新值
也就是说,AtomicInteger 首先根据 objectFieldOffset() 方法确定 value 属性在对象上的偏移值。然后将对象本身,偏移值,期望值,新值作为参数传递过去。在 compareAndSwapInt() 方法中:首先根据对象确定内存,然后根据偏移值获取到要操作的内存地址,直接拿期望值和内存中的值做比较,如果相等的话就将新值写入内存。
从这里也就可以看出 Unsafe 类直接通过操作内存完成 CAS 操作,调用的方法本身又是 native 方法,因此操作本身就是原子的,也就是说不会出现线程安全问题。
有了上面的铺垫,我们再来看另一个 AtomicInteger 常用的方法源码:
AtomicInteger 源码:
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
Unsafe 源码:
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
public native int getIntVolatile(Object var1, long var2)