Unsafe类使Java拥有了像C语言的指针一样操作内存空间的能力,同时也带来了指针的问题。可以通过类似 C语言里面的 memcpy 和 memset等函数直接操作对应内存地址的数据。
Unsafe是单例模式,通过调用getUnsafe方法来获取Unsafe实例。但是在方法中通过判断ClassLoader是不是启动类BootStrap ClassLoader来决定是否返回,从而限制了用户去调用Unsafe实例的获取。原因应该就是它能直接操作内存,这交给用户肯定是不安全的。这也是java抛弃指针的原因。
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
但是可以通过反射的方式来创建一个实例来:
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
通过这个Unsafe实例,可以进行内存管理、线程挂起和恢复、多线程同步、内存屏障等。具体方法列表详见https://www.cnblogs.com/throwable/p/9139947.html
内存管理
假设有个类A,它拥有一个属性value。利用Unsafe,可以通过修改内存的方式操纵对象属性给value赋值。
class A{
private int value;
public A() {}
public int getValue() { return value; }
}
通过下面这个方法可以计算value属性值的内存偏移量:
public native long objectFieldOffset(Field f);
按照以下方式即可修改属性值:
A a = new A();
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe)field.get(null);
Field value = A.class.getDeclaredField("value");
long Offset = unsafe.objectFieldOffset(value);
unsafe.setMemory(a,Offset,1, (byte)0x64);
System.out.println(a.getValue());
线程挂起与恢复
在多线程开发时,为了防止并发问题,可以通过park和unpark方法来进行线程的挂起和恢复。
public native void park(boolean isAbsolute, long time);阻塞当前线程直到出现以下三种种情况之一:一个用于unpark方法已经出现过(在此park方法调用之前已经调用过)、线程被中断或者time时间到期(也就是阻塞超时)。
public native void unpark(Object thread);
释放被park创建的在一个线程上的阻塞。
多线程同步
AQS(阻塞同步机制)和CAS(非阻塞同步机制)的底层就是由Unsafe实现的。
public final native boolean compareAndSwapObject(Object o, long offset, Object expected, Object x);
针对Object对象进行CAS操作。即是对应Java变量引用o,原子性地更新o中偏移地址为offset的属性的值为x,当且仅的偏移地址为offset的属性的当前值为expected才会更新成功返回true,否则返回false。
o:目标Java变量引用。
offset:目标Java变量中的目标属性的偏移地址。
expected:目标Java变量中的目标属性的期望的当前值。
x:目标Java变量中的目标属性的目标更新值。
内存屏障
public native void loadFence();
在该方法之前的所有读操作,一定在load屏障之前执行完成。
public native void storeFence();
在该方法之前的所有写操作,一定在store屏障之前执行完成
public native void fullFence();
在该方法之前的所有读写操作,一定在full屏障之前执行完成,这个内存屏障相当于上面两个(load屏障和store屏障)的合体功能。