A collection of methods for performing low-level, unsafe operations. Although the class and all methods are public, use of this class is limited because only trusted code can obtain instances of it.
这是 Unsafe 类的注释,可见 JDK 是不推荐我们直接使用 Unsafe 的实例的,它是一系列低层次(比如针对内存的直接操作)操作的方法集合;所谓的 Unsafe 的含义是该类的实例可以绕开 JVM 的束缚直接操作内存,并且提供 CPU 指令 CAS 原子操作级别的支持,如果被一些不了解情况的人使用,会出现意想不到的异常情况,往往是致命的。
Java 的设计是不能直接操作底层操作系统,而是通过 native 方法去操作,这也是 JVM 的核心能力;但是 Unsafe 提供了一些更加高效的方式来使用 CPU 指令和操作内存空间。
Unsafe 实例
// 私有化构造函数,禁止了直接 new
private Unsafe() {}
// 静态的不可变对象
private static final Unsafe theUnsafe = new Unsafe();
@CallerSensitive
public static Unsafe getUnsafe() { // 获取Unsafe实例
// 判断类加载器是不是 Bootstrap ClassLoader,不是的话直接抛出不安全的异常
Class<?> caller = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(caller.getClassLoader()))
throw new SecurityException("Unsafe");
return theUnsafe;
}
// 使用
Unsafe U = Unsafe.getUnsafe();
//返回对象成员属性在内存地址相对于此对象的内存地址的偏移量
public native long objectFieldOffset(Field f);
//获得给定对象的指定地址偏移量的值,与此类似操作还有:getInt,getDouble,getLong,getChar等
public native Object getObject(Object o, long offset);
//给定对象的指定地址偏移量设值,与此类似操作还有:putInt,putDouble,putLong,putChar等
public native void putObject(Object o, long offset, Object x);
//有序、延迟版本的putObjectVolatile方法,不保证值的改变被其他线程立即看到。只有在field被volatile修饰符修饰时有效
public native void putOrderedObject(Object o, long offset, Object x);
//绕过构造方法、初始化代码来创建对象
public native Object allocateInstance(Class<?> cls) throws InstantiationException;
// 本地方法,得搞明白 o 是什么,offset 又是什么
public native int getInt(Object o, long offset);
这个方法的是在给定的对象中,通过offset来获取对应位置成员变量的值。可以分为三种场景:
获取实例对象的成员
获取类的静态成员
获取数组类型的成员
CAS 操作
double-regiseter 模式是一种双地址模式,需要 o 和 offset 一起配合才能找到要操作的对象(或者内存地址),JVM 堆中的对象一般需要这种方式来操作。
什么是 CAS? 即比较并替换,实现并发算法时常用到的一种技术。CAS 操作包含三个操作数 —— 内存位置、预期原值及新值。执行 CAS 操作的时候,将内存位置的值与预期原值比较,如果相匹配,那么处理器会自动将该位置值更新为新值,否则,处理器不做任何操作。我们都知道,CAS 是一条 CPU 的原子指令(cmpxchg指令),不会造成所谓的数据不一致问题,Unsafe 提供的 CAS 方法(如compareAndSwapXXX)底层实现即为 CPU 指令 cmpxchg。
// 对象的 CAS 操作,o 的 offset 位置是一个对象成员,如果是excepted就更新为x
public final native boolean compareAndSwapObject(Object o, long offset,
Object expected,
Object x);
// 对象的 CAS 操作,o 的 offset 位置是一个int类型的成员
public final native boolean compareAndSwapInt(Object o, long offset,
int expected,
int x);
// 对象的 CAS 操作,o 的 offset 位置是一个long类型的成员
public final native boolean compareAndSwapLong(Object o, long offset,
long expected,
long x);
// 自旋+CAS的典型用法,lock-free算法的精髓所在
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
// 自旋判断是期望的最新值就更新,如果不是就自旋,直到成功
do {
// 取到最新的值
v = getIntVolatile(o, offset);
// 尝试增加 delta,因为在多线程情况下,有可能存在竞态条件,使用自旋不断尝试
} while (!compareAndSwapInt(o, offset, v, v + delta));
return v;
}
// 和 getAndAddInt 类似
public final long getAndAddLong(Object o, long offset, long delta) {
long v;
do {
v = getLongVolatile(o, offset);
} while (!compareAndSwapLong(o, offset, v, v + delta));
return v;
}
// 原子性的设置指定位置的值为新的值,同样是自旋 + CAS
public final int getAndSetInt(Object o, long offset, int newValue) {
int v;
do {
v = getIntVolatile(o, offset);
} while (!compareAndSwapInt(o, offset, v, newValue));
return v;
}
public final long getAndSetLong(Object o, long offset, long newValue) {
long v;
do {
v = getLongVolatile(o, offset);
} while (!compareAndSwapLong(o, offset, v, newValue));
return v;
}
public final Object getAndSetObject(Object o, long offset, Object newValue) {
Object v;
do {
v = getObjectVolatile(o, offset);
} while (!compareAndSwapObject(o, offset, v, newValue));
return v;
}
线程调度操作
// 这是和线程调度相关的本地方法,和线程的状态切换有关系
// unpark 是将某个线程从 WAITING or TIMED_WAITING 状态激活,线程重新可以被调度,在就绪队列排队
// 竞争CPU
public native void unpark(Object thread);
// park 将某个线程从 Runnable 状态转成 WAITING or TIMED_WAITING,
// 线程等待被激活或者超时时间到后进入就绪队列
public native void park(boolean isAbsolute, long time);
// 方法 park、unpark 即可实现线程的挂起与恢复,将一个线程进行挂起是通过 park 方法实现的,
// 调用 park 方法后,线程将一直阻塞直到超时或者中断等条件出现;
// unpark 可以终止一个挂起的线程,使其恢复正常。
// 从对象的指定偏移量处获取变量的引用,使用 volatile 的加载语义
public native Object getObjectVolatile(Object o, long offset);
// 存储变量的引用到对象的指定的偏移量处,使用 volatile 的存储语义
public native void putObjectVolatile(Object o, long offset, Object x);
public native int getIntVolatile(Object o, long offset);
public native void putIntVolatile(Object o, long offset, int x);
// ...
// 内存屏障,禁止load操作重排序。屏障前的load操作不能被重排序到屏障后,屏障后的load操作不能被重排序到屏障前
// 这就保证了对某个变量的读,然后才能写
public native void loadFence();
// 内存屏障,禁止store操作重排序。屏障前的store操作不能被重排序到屏障后,屏障后的store操作不能被重排序到屏障前
// 这就保证了对某个变量的写,然后才能读
public native void storeFence();
//内存屏障,禁止load、store操作重排序
public native void fullFence();
StampedLock 是一个改进版的读写锁,内部使用就使用了内存屏障,可以去看看。
内存管理(堆外内存)
//分配内存, 相当于C++的malloc函数
public native long allocateMemory(long bytes);
//扩充内存
public native long reallocateMemory(long address, long bytes);
//释放内存
public native void freeMemory(long address);
//在给定的内存块中设置值
public native void setMemory(Object o, long offset, long bytes, byte value);
//内存拷贝
public native void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes);
//获取给定地址值,忽略修饰限定符的访问限制。与此类似操作还有: getInt,getDouble,getLong,getChar等
public native Object getObject(Object o, long offset);
//为给定地址设置值,忽略修饰限定符的访问限制,与此类似操作还有: putInt,putDouble,putLong,putChar等
public native void putObject(Object o, long offset, Object x);
//获取给定地址的byte类型的值(当且仅当该内存地址为allocateMemory分配时,此方法结果为确定的)
public native byte getByte(long address);
//为给定地址设置byte类型的值(当且仅当该内存地址为allocateMemory分配时,此方法结果才是确定的)
public native void putByte(long address, byte x);