Unsafe
介绍:
Java无法直接访问底层操作系统,而是通过本地方法(native)来访问。Java中的Unsafe类为我们提供了管理内存的能力。
创建Unsafe对象
Unsafe类是final的,不允许继承,而且构造函数是私有的,无法通过new的方式创建对象:
public final class Unsafe {
// 构造函数私有
private Unsafe() {}
...
}
但是我们可以通过反射的方式获取Unsafe对象:
static void testUnsafe() throws NoSuchFieldException, IllegalAccessException {
Class<?> unsafeClass = Unsafe.class;
Field theUnsafe = unsafeClass.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(unsafeClass);
System.out.println(unsafe);
}
Unsafe的主要功能
1,普通读写
可以通过Unsafe去读写一个类的属性,不管这个类是否私有(直接从内存读写)。
// var1: 要读的对象 var2: 对象的偏移地址
public native int getInt(Object var1, long var2);
// var1: 要读的对象 var2: 对象的偏移地址 var3: 新写入的数据
public native void putInt(Object var1, long var2, int var4);
getInt() 方法用于从对象的指定偏移地址处读取一个int类型的数据,需要指定要读取的对象和偏移量。
putInt() 方法用于在对象的指定地址出写入一个int类型的数据,需要执行要读取的对象、偏移量和新写入的值。其他类型的数据也有相同的方法,如Object和Boolean:
// 对象
public native Object getObject(Object var1, long var2);
public native void putObject(Object var1, long var2, Object var4);
// 布尔
public native boolean getBoolean(Object var1, long var2);
public native void putBoolean(Object var1, long var2, boolean var4);
2,Volatile读写
1步骤中的读写是普通的读写操作,无法保证可见性和有序性。而源码中相应的volatile方法就可保证有序性和可见性:
// volatile读
public native int getIntVolatile(Object var1, long var2);
// volatile写
public native void putIntVolatile(Object var1, long var2, int var4);
getIntVolatile方法的作用是在对象的指定位置volatile读一个int类型数据,putIntVolatile是在指定位置volatile写一个数据(volatile解析详见 02-关键字volatile解析)。
3,有序写入
有序写入只保证写入的有序性,并不保证可见性,也就是说,该线程的写入不保证其他线程能立马看到。Unsafe类中,只提供了三种类型的有序写方法:
public native void putOrderedObject(Object var1, long var2, Object var4);
public native void putOrderedInt(Object var1, long var2, int var4);
public native void putOrderedLong(Object var1, long var2, long var4);
4,CAS操作
CAS: Compare And Swap。Unsafe中提供了三种类型Object、int、long的CAS操作。
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);
java.util.concurrent中的一些类用到了大量的CAS操作,如AtomicInteger等原子类的写操作和ConcurrentHashMap等类的乐观锁。
5,获取偏移量
我们可以通过偏移量和对象得到对象中属性在内存中所处的位置。Unsafe提供了如下方式:
// 获取静态属性Field在对象中的偏移量
public native long staticFieldOffset(Field var1);
// 获取非静态属性在对象中的偏移量
public native long objectFieldOffset(Field var1);
// 返回Field所在对象
public native Object staticFieldBase(Field var1);
// 返回数组中第一个元素的偏移量
public native int arrayBaseOffset(Class<?> var1);
// 返回数组中第一个元素所占用的内存空间
public native int arrayIndexScale(Class<?> var1);
6,线程调度
public native void unpark(Object var1);
public native void park(boolean var1, long var2);
Unsafe中的park()和unpark()方法实现了对线程的挂起和唤醒。LockSupport中的park()和unpark()方法就是依靠Unsafe中的这两个方法实现的:
// 线程挂起
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L);// 挂起
setBlocker(t, null);
}
// 线程唤醒
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
7,直接内存操作
在学习JVM时,我们知道Java不可以直接堆内存进行操作,对象内存的分配、回收都是Java虚拟机实现的。Unsafe中提供了直接操作内存的能力。
// 分配内存
public native long allocateMemory(long var1);
// 重新分配内存
public native long reallocateMemory(long var1, long var3);
// 设置内存
public native void setMemory(Object var1, long var2, long var4, byte var6);
// 设置内存
public void setMemory(long var1, long var3, byte var5) {
this.setMemory((Object)null, var1, var3, var5);
}
// 复制
public native void copyMemory(Object var1, long var2, Object var4, long var5, long var7);
// 复制
public void copyMemory(long var1, long var3, long var5) {
this.copyMemory((Object)null, var1, (Object)null, var3, var5);
}
// 清除内存
public native void freeMemory(long var1);
8,类加载
// 定义一个类,用于动态创建类
public native Class<?> defineClass(String var1, byte[] var2, int var3, int var4, ClassLoader var5, ProtectionDomain var6);
// 用于动态创建一个匿名内部类
public native Class<?> defineAnonymousClass(Class<?> var1, byte[] var2, Object[] var3);
// 用于创建一个类的实例,但是不会调用这个实例的构造方法,如果这个类还未被初始化,则初始化这个类
public native Object allocateInstance(Class<?> var1) throws InstantiationException;
// 判断是否需要初始化一个类
public native boolean shouldBeInitialized(Class<?> var1);
// 保证一个类已经被初始化
public native void ensureClassInitialized(Class<?> var1);
9,内存屏障
// 保证这个屏障之前所有读操作已经完成
public native void loadFence();
// 保证这个屏障之前所有写操作已经完成
public native void storeFence();
// 保证这个屏障之前所有读写操作都完成
public native void fullFence();
参考博客:https://www.jianshu.com/p/db8dce09232d