CAS概述

Unsafe 是 java 留给开发者的后门,用于直接操作系统内存且不受 jvm 管辖,实现类似 C++ 风格的操作。

Oracle 官方一般不建议开发者使用 Unsafe 类,因为正如这个类的类名一样,它并不安全,使用不当会造成内存泄露。

在平时的业务开发中,这个类基本是不会有接触到的,但是在 java 的并发包和众多偏向底层的框架中,都有大量应用。

值得一提的是,该类的大部分方法均为 native 修饰,即为直接调用的其它语言(大多为 C++)编写的方法来进行操作,很多细节无法追溯,只能大致了解。

本篇演示Unsafe中的CAS使用

CAS对象获取方式

官方获取方式

private static final Unsafe unsafe = Unsafe.getUnsafe();
  • 如AQS AbstractQueuedSynchronizer直接通过Unsafe.getUnsafe()的方式获取Unsafe对象
  • 但是三方程序通过上述方式获取时,会抛出异常Caused by: java.lang.SecurityException: Unsafe
@CallerSensitive
public static Unsafe getUnsafe() {
    Class<?> caller = Reflection.getCallerClass();
    if (!VM.isSystemDomainLoader(caller.getClassLoader()))
        throw new SecurityException("Unsafe");
    return theUnsafe;
}
  • 在getUnsafe()方法中,程序会通过Reflection.getCallerClass()获取到调用者的class,判断该class的ClassLoader是否是系统类加载器
  • jdk有三种类加载器,分别为 系统类加载器,扩展类加载器,应用类加载器
  • 上述方法getUnsafe()只允许通过系统类加载器加载的调用者调用,其它的类加载器加载的调用者会直接抛出异常
  • 即,jdk的开发者,不希望开发者使用该类

反射获取方式

Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeInstance.setAccessible(true);
unsafe = (Unsafe) theUnsafeInstance.get(Unsafe.class);
······
unsafe.compareAndSwapInt(app, valueOffset, 1, 2);
  • 可通过反射的方式获取到Unsafe
  • 注意unsafe.compareAndSwapInt(app, valueOffset, 1, 2);使用的方法一定与值的类型匹配
    • compareAndSwapInt()方法时对应的1和2应为int类型
    • compareAndSwapLong()方法时,对应的1和2应为long类型