前言
Unsafe 類一直是個(gè)很神秘的角色,我們普通開發(fā)者幾乎不會(huì)碰到,頂多也是使用了并發(fā)包之類的系統(tǒng)類庫(kù),間接使用到了而已。那它到底是用來(lái)做什么的呢?它提供了我們直接操作內(nèi)存的接口。Java 本身作為一個(gè)內(nèi)存自動(dòng)管理的工具,內(nèi)存的開辟釋放由虛擬機(jī)代為管理,然而,HotSpot 的設(shè)計(jì)者留下了 Unsafe 的類,用于擴(kuò)展,它可以直接開辟內(nèi)存,釋放內(nèi)存,讀取任意地址的內(nèi)存,而不受 Java 堆內(nèi)存的限制。盡管如此,它并不能為我們所用,加載這個(gè)類只能由系統(tǒng)的類加載器執(zhí)行,但我們可以通過反射獲取到它的實(shí)例對(duì)象,Java 反射的確很牛啊。
如何獲取實(shí)例對(duì)象
第一個(gè)問題:為什么我們需要通過 Field 獲取,不能使用 newInstance 獲取呢?
通過 newInstance 的方法獲取實(shí)力需要構(gòu)造函數(shù)是 public 的,否則會(huì)拋異常,及時(shí) getUnsafe 是靜態(tài)函數(shù),我們也不能通過這個(gè)去獲取,因?yàn)檫@時(shí)候類加載不是系統(tǒng)的,會(huì)拋異常
private Unsafe() {
}
@CallerSensitive
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
Unsafe unsafe = Unsafe.class.newInstance();
由于 Unsafe 是單例,當(dāng)類加載時(shí),theUnsafe 實(shí)例會(huì)被加載,這樣我們就可以通過反射獲取這個(gè)實(shí)例
static {
registerNatives();
Reflection.registerMethodsToFilter(Unsafe.class, new String[]{"getUnsafe"});
// 重點(diǎn)
theUnsafe = new Unsafe();
}
通過 Field 獲取
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
}
獲取到這實(shí)例,就可以干很多事情了.....
先來(lái)看看 API 圖



太多了,眼睛都看瞎了
歸個(gè)類吧
- 直接操作內(nèi)存,將某個(gè)對(duì)象的值更改,讀取,比如將 String 的 value 更改,是不是很神奇,這個(gè)是用反射也可以更改哦!下次面試的時(shí)候可以考考別人 String 的 value 怎么才能改的掉,看他會(huì)幾種。
String str = "Hello Unsafe";
Field value = str.getClass().getDeclaredField("value");
unsafe.putObject(str, unsafe.objectFieldOffset(value), new char[]{'M', 'a', 'j', 'i', 'c'});]
System.out.println(str);
// output: Majic
// throw exception
String str = "Hello Unsafe";
Field value = str.getClass().getDeclaredField("value");
value.setAccessible(true);
value.set(str, new char[] {'f', 'i', 'n', 'a', 'l'});
- CompareAndSwap 著名的 CAS
為了保證并發(fā)安全, CAS 涉及到的變量應(yīng)該使用 volatile 修飾,保證讀到的值最新
allocateInstance 新建一個(gè)沒有初始化的實(shí)例,各個(gè)值都是默認(rèn)值
compareAndSwapLong 第一個(gè)參數(shù)是 object,第二個(gè)參數(shù)是變量?jī)?nèi)存偏移值,可以用 unsafe 類獲取實(shí)際偏移值,第三個(gè)參數(shù)是 期望值,第四個(gè)三叔目標(biāo)值,大致的語(yǔ)義就是:如果內(nèi)存中是期望值,我就更新為目標(biāo)值,否則不更新
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
InnerClass o = (InnerClass)unsafe.allocateInstance(InnerClass.class);
o.print(); // print 100
Field a = o.getClass().getDeclaredField("value");
unsafe.putLong(o, unsafe.objectFieldOffset(a), 10000);
o.print(); // print 10000
unsafe.compareAndSwapLong(o, unsafe.objectFieldOffset(a), 10000, 1111);
o.print(); // print 1111
unsafe.compareAndSwapLong(o, unsafe.objectFieldOffset(a), 1000, 10000);
o.print(); // print 1111
}
static class InnerClass {
// 保證內(nèi)存可見性
private volatile long value;
InnerClass() {
value = 100L;
}
void print() {
System.err.println("value==>" + value);
}
}
- 線程掛起,取消
使用 unsafe.unpark 可以取消 Thread.sleep() 后者 park 的線程
使用 unsafe.park(false, 1000000000) 可以掛起當(dāng)前線程,第一個(gè)參數(shù)表示是isAbsolute,是否是絕對(duì)時(shí)間,后一個(gè)參數(shù)為時(shí)間,flase 表示相對(duì)時(shí)間,0表示一直掛起,單位為納秒;true 表示絕對(duì)時(shí)間,單位為毫秒,System.currentThreadMillis 搭配使用
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException, InterruptedException {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
InnerThread innerThread = new InnerThread(unsafe);
innerThread.run();
Thread.sleep(20);
// 取消掛起
unsafe.unpark(innerThread);
}
static class InnerThread extends Thread {
Unsafe unsafe;
InnerThread(Unsafe unsafe) {
this.unsafe = unsafe;
}
@Override
public void run() {
System.out.println("start");
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
//unsafe.park(false, 1000000000L);
System.out.println("end");
}
}
小結(jié)
以后又可以吹一波了,一箭雙雕,反射,Unsafe