java-juc-unsafe

67 阅读3分钟

unsafe

unsafe-cas

unsafe实现cas的原理:使用cpu的原子指令cmpxchg对指定内存地址cas.

// AtomicInteger实现CAS的原理
public class AtomicInteger extends Number implements java.io.Serializable {
    // 实现CAS的工具类
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    // 保证值内存可见
    private volatile int value;
    // cas作用的内存偏移地址
    private static final long valueOffset;
    static {
        try {
            valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }
}
// CAS
public final class Unsafe {
    // Unsafe是单例
    private static final Unsafe theUnsafe;
    private Unsafe() {}
    static {
        registerNatives();
        Reflection.registerMethodsToFilter(Unsafe.class, new String[]{"getUnsafe"});
        theUnsafe = new Unsafe();
    }
    // 注解表明需要引导类加载器,调用这个方法的类需要是BootstrapClassLoader加载的类
    // 不能自己加载,保证单例
    @CallerSensitive
    public static Unsafe getUnsafe() {
        Class var0 = Reflection.getCallerClass();
        // 仅在引导类加载器`BootstrapClassLoader`加载时才合法
        if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
            throw new SecurityException("Unsafe");
        } else {
            return theUnsafe;
        }
    }
    // 返回对象成员属性field在内存地址相对于此对象的内存地址的偏移量
    public native long objectFieldOffset(Field var1);
    /**
    * cas实现的主要native操作
    * var1:对象内存地址
    * var2:field内存地址相对于对象内存地址的偏移
    * var4: 期望值
    * var5: 新值
    */
    public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
    public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
    public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
    // 废弃的方法
    @Deprecated
    public native void monitorEnter(Object var1);
    @Deprecated
    public native void monitorExit(Object var1);
}

Unsafe是位于 sun.misc 包下的一个类。Unsafe 提供的 API大致可分为内存操作(读/写)、CAS、Class相关(地址,construct)、对象操作、线程调度、系统信息获取、内存屏障、数组操作等几类,大部分是native方法。

应用场景: java.util.concurrent.atomic相关类、AQS、ConcurrentHashMap等相关类.

CAS 是实现并发算法时常用到的一种技术。CAS 操作包含三个操作数——内存位置、预期原值及新值。执行 CAS 操作的时候,将内存位置的值与预期原值比较,如果相匹配,那么处理器会自动将该位置值更新为新值,否则,处理器不做任何操作。我们都知道,CAS 是一条 CPU 的 原子指令(cmpxchg 指令),不会造成所谓的数据不一致问`题,Unsafe 提供的 CAS 方法(如 compareAndSwapXXX)底层实现即为 CPU 指令 cmpxchg。

对象的基地址 baseAddress+valueOffset 得到 value 的内存地址 valueAddress;基于cpu内存地址的原子性。不是JVM地址。

获取Unsafe实例的方法:@CallerSensitive

  1. 从 getUnsafe 方法的使用限制条件出发,通过 Java 命令行命令 -Xbootclasspath/a 把调用 Unsafe 相关方法的类 A 所在 jar 包路径追加到默认的 bootstrap 路径中,使得 A 被引导类加载器加载,从而通过 Unsafe.getUnsafe 方法安全的获取 Unsafe 实例。

java -Xbootclasspath/a: ${path} // 其中path为调用Unsafe相关方法的类所在jar包路径

  1. 反射实现
public class UnsafeSample {
    private Integer id;
    public static Unsafe getUnsafeByReflect() {
        try {
            // 启动时已经加载Unsafe实例,可以直接通过反射拿到
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            return (Unsafe) field.get(null);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
            return null;
        }
    }
    public static void main(String[] args) {
        try {
            // 获取unsafe工具类
            Unsafe unsafe = getUnsafeByReflect();
            // 获取对象的field
            Field idField = UnsafeSample.class.getDeclaredField("id");
            // 获取field的内存偏移地址
            System.out.println("id的内存偏移地址=" + unsafe.objectFieldOffset(idField));
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}

unsafe-usage

unsafe的其他应用场景

ps

  1. unsafe