UnSafe类方法和使用简介
- 参考文献
1. CAS方法
交换int、long和对象:
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);
2. 指令重拍
防止指令重排的写操作,JDK会在执行这三个方法时插入StoreStore内存屏障,避免发生写操作重排序:
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);
3. 获取没有访问权限的实例变量getObject(Obj,offset)
- 非系统类需要通过反射获取
Unsafe
实例; - 通过
Unsafe的 getObject(Obj,offset)
方法获取私有变量值。示例如下:
public class PrivateGetterDemo {
private static final Unsafe UNSAFE;
private static final long noGetNameOffset;
static{
try{
//fixme 非系统类使用反射获取UNSAFE对象
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
UNSAFE = (Unsafe) f.get(null);
noGetNameOffset=UNSAFE.objectFieldOffset(PrivateDemo.class.getDeclaredField("noGetname"));
}catch (Throwable e){
throw new Error(e);
}
}
public static void main(String[] args) {
//fixme 理论上没有访问权限的实例变量通过Unsafe获取
PrivateDemo pd=new PrivateDemo("pd");
System.out.println(UNSAFE.getObject(pd, noGetNameOffset));
}
}
4. 线程的挂起和重新开始:可以指定挂起时间
线程挂起和终止线程挂起方法:LockSupport类实现使用这两个方法;
//线程调用该方法,线程将一直阻塞直到超时,或者是中断条件出现;
//1.isAbsolute指使用的时间是相对时间还是绝对时间,false为相对时间;
//2.时间单位是纳秒;
public native void park(boolean isAbsolute, long time);
//终止挂起的线程,恢复正常.
public native void unpark(Object thread);
5.锁定对象
使用一个监视器对象进行加锁、解锁。
55 //锁定对象,必须是没有被锁的
56 public native void monitorEnter(Object o);
57
58 //解锁对象
59 public native void monitorExit(Object o);
60
61 //试图锁定对象,返回true或false是否锁定成功,如果锁定,必须用monitorExit解锁
62 public native boolean tryMonitorEnter(Object o);
6. 创建实例对象
82 //创建一个类的实例,不需要调用它的构造函数、初使化代码、各种JVM安全检查以及其它的一些底层的东西。即使构造函数是私有,我们也可以通过这个方法创建它的实例,对于单例模式,简直是噩梦,哈哈
83 public native Object allocateInstance(Class cls) throws InstantiationException;
7. 该方法获取对象中offset偏移地址对应的整型field的值,支持volatile load语义
public native Object getObjectVolatile(Object var1, long var2);
public native void putObjectVolatile(Object var1, long var2, Object var4);
8. park()和unPark()
使用示例
线程挂起和终止线程挂起方法。
FutureTask
在三种情况下使用LockSupport
挂起和释放线程:
private int awaitDone(boolean timed, long nanos)方法中:
1.如果指定了挂起时间: LockSupport.parkNanos(this, nanos);
2.如果没有指定挂起时间: LockSupport.park(this);
private void finishCompletion()方法中:移除和signal所有等待的线程
1. 将链表中引用指向null-此时并未释放线程,线程对象仍在阻塞;
2. 恢复挂起的线程,让其结束执行。
Thread t = q.thread;
if (t != null) {
q.thread = null;
LockSupport.unpark(t);
}
LockSupport
中三种方法的实现,都是通过调用Unsafe
的park(boolean isAbsolutely,long nanoTime)
和void unPark(Object thread)
//恢复线程
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
//挂起线程(绑定挂起的线程和相应的阻塞对象)
public static void parkNanos(Object blocker, long nanos) {
if (nanos > 0) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, nanos);
setBlocker(t, null);
}
}
//获取相对偏移量
private static final long parkBlockerOffset;
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> tk = Thread.class;
parkBlockerOffset = UNSAFE.objectFieldOffset
(tk.getDeclaredField("parkBlocker"));
//通过相对偏移量更新Thread的变量parkBlocker
private static void setBlocker(Thread t, Object arg) {
// Even though volatile, hotspot doesn't need a write barrier here.
UNSAFE.putObject(t, parkBlockerOffset, arg);
}
//Thread类中变量
volatile Object parkBlocker;