Java中的Unsafe

Java和C++語言的一個重要區(qū)別就是Java中我們無法直接操作一塊內(nèi)存區(qū)域讯沈,不能像C++中那樣可以自己申請內(nèi)存和釋放內(nèi)存婿奔。Java中的Unsafe類為我們提供了類似C++手動管理內(nèi)存的能力萍摊。
Unsafe類,全限定名是sun.misc.Unsafe穷劈,從名字中我們可以看出來這個類對普通程序員來說是“危險”的,一般應(yīng)用開發(fā)者不會用到這個類社证。

Unsafe類是"final"的练湿,不允許繼承。且構(gòu)造函數(shù)是private的:

public final class Unsafe {
    private static final Unsafe theUnsafe;
    public static final int INVALID_FIELD_OFFSET = -1;

    private static native void registerNatives();
    // 構(gòu)造函數(shù)是private的辽俗,不允許外部實例化
    private Unsafe() {
    }
    ...
}

因此我們無法在外部對Unsafe進行實例化崖飘。

獲取Unsafe

Unsafe無法實例化杈女,那么怎么獲取Unsafe呢?答案就是通過反射來獲取Unsafe:

public Unsafe getUnsafe() throws IllegalAccessException {
    Field unsafeField = Unsafe.class.getDeclaredFields()[0];
    unsafeField.setAccessible(true);
    Unsafe unsafe = (Unsafe) unsafeField.get(null);
    return unsafe;
}

主要功能

Unsafe的功能如下圖:

image

普通讀寫

通過Unsafe可以讀寫一個類的屬性,即使這個屬性是私有的啰劲,也可以對這個屬性進行讀寫蝇裤。

讀寫一個Object屬性的相關(guān)方法

public native int getInt(Object var1, long var2);

public native void putInt(Object var1, long var2, int var4);

getInt用于從對象的指定偏移地址處讀取一個int。putInt用于在對象指定偏移地址處寫入一個int恋拍。其他的primitive type也有對應(yīng)的方法藕甩。

Unsafe還可以直接在一個地址上讀寫

public native byte getByte(long var1);

public native void putByte(long var1, byte var3);

getByte用于從指定內(nèi)存地址處開始讀取一個byte。putByte用于從指定內(nèi)存地址寫入一個byte悯姊。其他的primitive type也有對應(yīng)的方法悯许。

volatile讀寫

普通的讀寫無法保證可見性和有序性辉阶,而volatile讀寫就可以保證可見性和有序性瘩扼。

public native int getIntVolatile(Object var1, long var2);

public native void putIntVolatile(Object var1, long var2, int var4);

getIntVolatile方法用于在對象指定偏移地址處volatile讀取一個int集绰。putIntVolatile方法用于在對象指定偏移地址處volatile寫入一個int谆棺。

volatile讀寫相對普通讀寫是更加昂貴的,因為需要保證可見性和有序性碍岔,而與volatile寫入相比putOrderedXX寫入代價相對較低类早,putOrderedXX寫入不保證可見性捏肢,但是保證有序性饥侵,所謂有序性躏升,就是保證指令不會重排序。

有序?qū)懭?/h2>

有序?qū)懭胫槐WC寫入的有序性,不保證可見性藕赞,就是說一個線程的寫入不保證其他線程立馬可見。

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);

直接內(nèi)存操作

我們都知道Java不可以直接對內(nèi)存進行操作双霍,對象內(nèi)存的分配和回收都是由JVM幫助我們實現(xiàn)的批销。但是Unsafe為我們在Java中提供了直接操作內(nèi)存的能力均芽。

// 分配內(nèi)存
public native long allocateMemory(long var1);
// 重新分配內(nèi)存
public native long reallocateMemory(long var1, long var3);
// 內(nèi)存初始化
public native void setMemory(long var1, long var3, byte var5);
// 內(nèi)存復(fù)制
public native void copyMemory(Object var1, long var2, Object var4, long var5, long var7);
// 清除內(nèi)存
public native void freeMemory(long var1);

CAS相關(guān)

JUC中大量運用了CAS操作,可以說CAS操作是JUC的基礎(chǔ)深纲,因此CAS操作是非常重要的湃鹊。Unsafe中提供了int,long和Object的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);

CAS一般用于樂觀鎖,它在Java中有廣泛的應(yīng)用怀愧,ConcurrentHashMap余赢,ConcurrentLinkedQueue中都有用到CAS來實現(xiàn)樂觀鎖。

偏移量相關(guān)

public native long staticFieldOffset(Field var1);

public native long objectFieldOffset(Field var1);

public native Object staticFieldBase(Field var1);

public native int arrayBaseOffset(Class<?> var1);

public native int arrayIndexScale(Class<?> var1);

staticFieldOffset方法用于獲取靜態(tài)屬性Field在對象中的偏移量毕贼,讀寫靜態(tài)屬性時必須獲取其偏移量。objectFieldOffset方法用于獲取非靜態(tài)屬性Field在對象實例中的偏移量蛤奢,讀寫對象的非靜態(tài)屬性時會用到這個偏移量鬼癣。staticFieldBase方法用于返回Field所在的對象。arrayBaseOffset方法用于返回數(shù)組中第一個元素實際地址相對整個數(shù)組對象的地址的偏移量啤贩。arrayIndexScale方法用于計算數(shù)組中第一個元素所占用的內(nèi)存空間待秃。

線程調(diào)度

public native void unpark(Object var1);

public native void park(boolean var1, long var2);

public native void monitorEnter(Object var1);

public native void monitorExit(Object var1);

public native boolean tryMonitorEnter(Object var1);

park方法和unpark方法相信看過LockSupport類的都不會陌生,這兩個方法主要用來掛起和喚醒線程痹屹。LockSupport中的park和unpark方法正是通過Unsafe來實現(xiàn)的:

// 掛起線程
public static void park(Object blocker) {
    Thread t = Thread.currentThread();
    setBlocker(t, blocker); // 通過Unsafe的putObject方法設(shè)置阻塞阻塞當(dāng)前線程的blocker
    UNSAFE.park(false, 0L); // 通過Unsafe的park方法來阻塞當(dāng)前線程章郁,注意此方法將當(dāng)前線程阻塞后,當(dāng)前線程就不會繼續(xù)往下走了志衍,直到其他線程unpark此線程
    setBlocker(t, null); // 清除blocker
}

// 喚醒線程
public static void unpark(Thread thread) {
    if (thread != null)
        UNSAFE.unpark(thread);
}

monitorEnter方法和monitorExit方法用于加鎖暖庄,Java中的synchronized鎖就是通過這兩個指令來實現(xiàn)的楼肪。

類加載

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);

defineClass方法定義一個類培廓,用于動態(tài)地創(chuàng)建類。
defineAnonymousClass用于動態(tài)的創(chuàng)建一個匿名內(nèi)部類春叫。
allocateInstance方法用于創(chuàng)建一個類的實例肩钠,但是不會調(diào)用這個實例的構(gòu)造方法,如果這個類還未被初始化暂殖,則初始化這個類价匠。
shouldBeInitialized方法用于判斷是否需要初始化一個類。
ensureClassInitialized方法用于保證已經(jīng)初始化過一個類呛每。

內(nèi)存屏障

public native void loadFence();

public native void storeFence();

public native void fullFence();

loadFence:保證在這個屏障之前的所有讀操作都已經(jīng)完成踩窖。
storeFence:保證在這個屏障之前的所有寫操作都已經(jīng)完成。
fullFence:保證在這個屏障之前的所有讀寫操作都已經(jīng)完成晨横。

轉(zhuǎn)載:http://www.reibang.com/p/db8dce09232d

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末毙石,一起剝皮案震驚了整個濱河市廉沮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌徐矩,老刑警劉巖滞时,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異滤灯,居然都是意外死亡坪稽,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門鳞骤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來窒百,“玉大人,你說我怎么就攤上這事豫尽「萆遥” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵美旧,是天一觀的道長渤滞。 經(jīng)常有香客問我,道長榴嗅,這世上最難降的妖魔是什么妄呕? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮嗽测,結(jié)果婚禮上绪励,老公的妹妹穿的比我還像新娘。我一直安慰自己唠粥,他們只是感情好疏魏,可當(dāng)我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著晤愧,像睡著了一般大莫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上养涮,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天葵硕,我揣著相機與錄音眉抬,去河邊找鬼贯吓。 笑死,一個胖子當(dāng)著我的面吹牛蜀变,可吹牛的內(nèi)容都是我干的悄谐。 我是一名探鬼主播,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼库北,長吁一口氣:“原來是場噩夢啊……” “哼爬舰!你這毒婦竟也來了们陆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤情屹,失蹤者是張志新(化名)和其女友劉穎坪仇,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體垃你,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡椅文,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了惜颇。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片皆刺。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖凌摄,靈堂內(nèi)的尸體忽然破棺而出羡蛾,到底是詐尸還是另有隱情,我是刑警寧澤锨亏,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布痴怨,位于F島的核電站,受9級特大地震影響屯伞,放射性物質(zhì)發(fā)生泄漏腿箩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一劣摇、第九天 我趴在偏房一處隱蔽的房頂上張望珠移。 院中可真熱鬧,春花似錦末融、人聲如沸钧惧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽浓瞪。三九已至,卻和暖如春巧婶,著一層夾襖步出監(jiān)牢的瞬間乾颁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工艺栈, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留英岭,地道東北人。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓湿右,卻偏偏與公主長得像诅妹,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內(nèi)容

  • http://www.reibang.com/p/db8dce09232d[https://www.jiansh...
    瘋狂擼代碼的奮青騷年閱讀 240評論 0 0
  • Java和C++語言的一個重要區(qū)別就是Java中我們無法直接操作一塊內(nèi)存區(qū)域吭狡,不能像C++中那樣可以自己申請內(nèi)存和...
    zhong0316閱讀 44,863評論 3 47
  • java不能直接訪問操作系統(tǒng)底層尖殃,而是通過本地方法來訪問。Unsafe類提供了硬件級別的原子操作划煮。Java和C++...
    笨比喬治閱讀 214評論 0 1
  • Unsafe類 不能直接 new, 其構(gòu)造函數(shù)被私有化 public final class Unsafe : U...
    lj72808up閱讀 450評論 0 0
  • 前一段時間在研究juc源碼的時候送丰,發(fā)現(xiàn)在很多工具類中都調(diào)用了一個Unsafe類中的方法,出于好奇就想要研究一下這個...
    碼農(nóng)參上閱讀 898評論 1 1