sun.misc.Unsafe中一些經常使用方法記錄

sun.misc.Unsafe中一些經常使用方法記錄

前情摘要

sun公司提供了能夠用於直接操做內存的類,這個類就是sun.misc.Unsafe。由於Java自己是不會涉及到直接操做內存的,Java API也沒有提供這些操做,內存管理所有交給虛擬機來作。Sun之因此提供這個類,由於有些功能現有的Java API知足不了,若是沒有這個類,可能就沒有如今原子類,J.U.C包了,也許也沒了各類Concurrent Collection類,可能也沒了NIO的堆外內存,因此這個類十分的有用,而且很重要,Sun也沒有開放這個類的源代碼,而且對它的使用也作了一些限制。java

經過反編譯看到這個類中,幾乎全部的方法都是native修飾的,即便其餘非native修飾的,最後也是在調用本類的其餘native方法。api

Unsafe提供了一個static修飾的靜態方法,用來獲取這個類的實例,這是一個單例。緩存

@CallerSensitive
   public static Unsafe getUnsafe() {
        Class var0 = Reflection.getCallerClass();
        if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
            throw new SecurityException("Unsafe");
        } else {
            return theUnsafe;
        }
    }

這個getUnsafe()方法並非直接返回Unsafe的實例,而是作了校驗。經過校驗直接使用getUnsafe這個方法的類的類加載器是否爲Bootstrap類加載器,來作安全檢查。因爲Java API中的類都是由Bootstrap類加載器加載的,因此是能夠直接調用這個方法來獲取Unsafe的實例。不過若是要在本身的代碼中使用Unsafe,則須要經過反射修改theUnsafe字段的訪問修飾符,而後獲取Unsafe的實例。因爲Unsafe能夠直接操做內存,因此存在很大的風險,使用時須要特別謹慎!安全

想閱讀下關於原子類和J.U.C包下的一些實現,發現這些常用的方法,記錄下每一個方法的具體含義,否則無法理解整個客戶端方法的行爲。多線程

整個Unsafe類中共分爲如下幾類操做:併發

  • 底層內存信息相關的 好比內存頁大小
  • 操做對象及其字段 如:實例化對象,獲取實例域的偏移量
  • 操做類以及靜態字段 如:定義一個類,獲取靜態字段的偏移量
  • 操做數據對象
  • 同步操做 提供操做監控器和CAS的支持
  • 內存操做 主要是內存的分配,複製,銷燬等 NIO中用到的多

JUC和原子類以及併發集合中主要用到如下這些方法

Unsafe.putObject()

將一個引用類型的值存入到給定對象的變量中,這個方法的參數列表爲putObject(Object o, long offset, Object x),這裏就是將對象x的引用,存到對象o的偏移量爲offset的變量上。要注意這個操做不保證內存可見性,也就是說對對象o的指定字段的更新,並不會在多線程環境下被其餘線程發現值的變更。因此提供了一個具備volatile語義的方法putObjectVolatile(),這個操做具備volatile語義的store語義,並不具有load的語義,若是想在獲取時具有load的語義,可使用getObjectVolatile()線程

Unsafe.park()

此方法主要用於阻塞當前線程,方法的參數爲park(boolean absolute, long time),若是absolute爲false,time爲0,則表示一直阻塞,直至unpark方法被調用,或者被中斷。若是absolute爲false,time不爲0,則表示爲給定的納秒事後,中斷阻塞,相應的線程能夠被系統調度。若是absolute爲true,time的單位爲毫秒,不過這是一個絕對時間,Epoch Time—— Unix紀元時間 1970.1.1 零時,absolute表示的絕對是指基於這個時間的絕對時間,也就是說park(true, System.currentTimeMillis()+N)這種寫法纔是對的,否則輸入的任意數字都沒有做用的,不會起到阻塞線程的效果。code

會將線程一直阻塞,直至如下狀況發生:對象

  • 當相應的unpark方法在park方法調用前被調用,park方法調用會被當即返回
  • 當相應的unpark方法在park方法後被調用,park方法返回
  • 在調用park方法的先後,若是檢測到線程已經被設置爲中斷,則park方法當即返回
  • absolute爲false而且time不爲0,所給的納秒已通過了
  • absolute爲true,而且所給的時間(必須是在Epoch紀元時間的基礎上,一般取當前距離紀元時間的毫秒數加上但願阻塞時間毫秒數)毫秒已經用完
  • 無理由返回

Unsafe.unpark()

將指定的線程從park阻塞狀態中恢復過來,方法僅有一個參數unpark(Thraed thread),要確保thread對象沒有被銷燬,也就是要檢查不爲null內存

Unsafe.getObjectVolatile()

獲取所給對象的所給變量的值,使用volatile語義的load語義,會在實際獲取這個值的時候從主存中加載,不會使用CPU緩存中的,總能確保獲取到的是有效的值。 getObjectVolatile(Object o, long offset)

Unsafe.getInt()

有三個重載方法getInt(Object o, long offset)getInt(long address)getIntVolatile(long address),都是從指定的位置獲取變量的值,只不過第一個的offset是相對於對象o的相對偏移量,第二個address是絕對地址偏移量。若是第一個方法中o爲null是,offset也會被做爲絕對偏移量。第三個則是帶有volatile語義的load讀操做。

Unsafe.putInt()

一樣有三個在用的重載方法,參數也差很少同putInt相似,含義則是相反,用於對指定內存地址的變量賦值。

putInt(Object o, long offset, int x)putInt(long address)putIntVolatile(Object o, long offset, int x),最後一個是Volatile版本的putInt方法。

Unsafe.objectFieldOffset()

這個方法的做用是用來獲取指定對象的域的偏移量,這個是指這個字段在內存中的位置相對於這個對象在內存中的起始地址的偏移量,也就是隔了多遠,有了這個值,後續就能直接定位到這個域的內存地址,而後獲取其中的值去操做。注意,這個方法只適用於域爲非static修飾的,static修飾的域須要使用Unsafe.staticFieldOffset()

Usage:

long offset = unsafe.objectFieldOffset(Field var) 方法結收一個java.lang.reflect.Field對象。

靜態域的偏移量也是同樣的使用,方法名不一樣。

Unsafe.compareAndSwapObject()

以CAS的方式來更新一個引用類型的字段值,若是更新成功則返回true,不然返回false。

Usage:

boolean casResult = unsafe.compareAndSwapObject(object,offset,expected,update);

這個方法自己不會有自旋行爲,直接作CAS操做,若是失敗當即返回。一般是咱們在代碼中經過無限循環實現CAS的自旋,而且須要被更新的域通常都是用volatile修飾的,否則多線程環境下沒法保證正確性。

若是想經過此方式來CAS操做靜態域的值,第一個參數爲靜態域所在的Class對象。

Unsafe.compareAndSwapInt()

和上面的方法做用同樣,只不過是用來操做int類型的變量

Unsafe.putOrderedInt()

Unsafe.putIntVolatile()的有序版本/延遲版本,只保證最終將制定的變量更新爲新的值。

Unsafe.getAndSetInt()

這個不屬於native方法,是用getIntVolatilecompareAndSwapInt兩個方法組合,對指定的變量設置爲新的值,不過會返回設置新值前的舊值,而不是無條件直接設置,在競爭的條件下,這樣就須要在循環中不斷作CAS。

Unsafe.getAndAddInt()

這個一樣不屬於native方法,和上面差很少,只不過是在舊值的基礎上作了個加新值的操做

相關文章
相關標籤/搜索