AQS 架構之 Unsafe 源碼詳解,aqsunsafe
■ 前言
之前 LockSupport 那篇已經敘述了是線程阻塞工具類,其底層由 Unsafe 實現,即 park(), unpark() 方法,擷取指標位移量,並操縱記憶體。本篇主要介紹 Unsafe 的源碼,看看底層到底做了什麼。
■ Unsafe 綜述
- 作用: Unsafe是個後門類,封裝了一些類似指標的操作,提供了一些可以直接操控記憶體和線程的底層操作
- 使用: Unsafe被JDK廣泛用於nio包和並發包中,但是不建議在生產環境使用,風險太大
- 不安全: 不安全指的是指標的操作不安全(Java因此才把指標去掉),若指標指錯位置或計算指標位移量出錯,結果不可想象,比如說覆蓋了別人的記憶體,那可能就GG思密達了...
- 補充1: 有機會開JVM番的話,筆者會從JVM源碼角度重新解析Unsafe一些重要方法的實現
- 補充2: 此番為 AQS 架構之綜述 (趕製中) 的子番
■ Unsafe 資料結構
1. 類定義
public final class Unsafe
2. 構造器
//私人構造器 --單例模式private Unsafe() {}
3. 重要變數
private static final Unsafe theUnsafe;public static final int INVALID_FIELD_OFFSET = -1;public static final int ARRAY_BOOLEAN_BASE_OFFSET;public static final int ARRAY_BYTE_BASE_OFFSET;public static final int ARRAY_SHORT_BASE_OFFSET;public static final int ARRAY_CHAR_BASE_OFFSET;public static final int ARRAY_INT_BASE_OFFSET;public static final int ARRAY_LONG_BASE_OFFSET;public static final int ARRAY_FLOAT_BASE_OFFSET;public static final int ARRAY_DOUBLE_BASE_OFFSET;public static final int ARRAY_OBJECT_BASE_OFFSET;public static final int ARRAY_BOOLEAN_INDEX_SCALE;public static final int ARRAY_BYTE_INDEX_SCALE;public static final int ARRAY_SHORT_INDEX_SCALE;public static final int ARRAY_CHAR_INDEX_SCALE;public static final int ARRAY_INT_INDEX_SCALE;public static final int ARRAY_LONG_INDEX_SCALE;public static final int ARRAY_FLOAT_INDEX_SCALE;public static final int ARRAY_DOUBLE_INDEX_SCALE;public static final int ARRAY_OBJECT_INDEX_SCALE;public static final int ADDRESS_SIZE;private static native void registerNatives();static { registerNatives(); Reflection.registerMethodsToFilter(Unsafe.class, new String[]{"getUnsafe"}); theUnsafe = new Unsafe();//單例模式 -餓漢式 ARRAY_BOOLEAN_BASE_OFFSET = theUnsafe.arrayBaseOffset(boolean[].class); ARRAY_BYTE_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class); ARRAY_SHORT_BASE_OFFSET = theUnsafe.arrayBaseOffset(short[].class); ARRAY_CHAR_BASE_OFFSET = theUnsafe.arrayBaseOffset(char[].class); ARRAY_INT_BASE_OFFSET = theUnsafe.arrayBaseOffset(int[].class); ARRAY_LONG_BASE_OFFSET = theUnsafe.arrayBaseOffset(long[].class); ARRAY_FLOAT_BASE_OFFSET = theUnsafe.arrayBaseOffset(float[].class); ARRAY_DOUBLE_BASE_OFFSET = theUnsafe.arrayBaseOffset(double[].class); ARRAY_OBJECT_BASE_OFFSET = theUnsafe.arrayBaseOffset(Object[].class); ARRAY_BOOLEAN_INDEX_SCALE = theUnsafe.arrayIndexScale(boolean[].class); ARRAY_BYTE_INDEX_SCALE = theUnsafe.arrayIndexScale(byte[].class); ARRAY_SHORT_INDEX_SCALE = theUnsafe.arrayIndexScale(short[].class); ARRAY_CHAR_INDEX_SCALE = theUnsafe.arrayIndexScale(char[].class); ARRAY_INT_INDEX_SCALE = theUnsafe.arrayIndexScale(int[].class); ARRAY_LONG_INDEX_SCALE = theUnsafe.arrayIndexScale(long[].class); ARRAY_FLOAT_INDEX_SCALE = theUnsafe.arrayIndexScale(float[].class); ARRAY_DOUBLE_INDEX_SCALE = theUnsafe.arrayIndexScale(double[].class); ARRAY_OBJECT_INDEX_SCALE = theUnsafe.arrayIndexScale(Object[].class); ADDRESS_SIZE = theUnsafe.addressSize();}
4. 重要方法
//獲得給定對象記憶體位移量的int值public native int getInt(Object var1, long var2);//設定給定對象記憶體位移量的int值public native void putInt(Object var1, long var2, int var4);public native Object getObject(Object var1, long var2);public native void putObject(Object var1, long var2, Object var4);//....還有Boolean、Byte、Char、Short、Long、Float、Double的get\set....//記憶體配置、釋放//分配記憶體public native long allocateMemory(long var1);//擴充記憶體public native long reallocateMemory(long var1, long var3);public native void setMemory(Object var1, long var2, long var4, byte var6);public void setMemory(long var1, long var3, byte var5) { this.setMemory((Object)null, var1, var3, var5);}//拷貝記憶體public native void copyMemory(Object var1, long var2, Object var4, long var5, long var7);public void copyMemory(long var1, long var3, long var5) { this.copyMemory((Object)null, var1, (Object)null, var3, var5);}//釋放記憶體public native void freeMemory(long var1);//擷取欄位在對象中的記憶體位移量public native long staticFieldOffset(Field var1);public native long objectFieldOffset(Field var1);public native Object staticFieldBase(Field var1);public native void ensureClassInitialized(Class<?> var1);//數組元素定位//arrayBaseOffset 和 arrayIndexScale 搭配使用可以定位元組中每個元素在記憶體中的位置//擷取數組第一個元素的位移地址public native int arrayBaseOffset(Class<?> var1);//擷取數組的轉換因子,也就是數組中元素的增量地址public native int arrayIndexScale(Class<?> var1);public native int addressSize();public native int pageSize();//類定義public native Class<?> defineClass(String var1, byte[] var2, int var3, int var4, ClassLoader var5, ProtectionDomain var6);public native Class<?> defineClass(String var1, byte[] var2, int var3, int var4);public native Class<?> defineAnonymousClass(Class<?> var1, byte[] var2, Object[] var3);//建立執行個體public native Object allocateInstance(Class<?> var1) throws InstantiationException;//Synchronized同步塊的指令實現 1.8版的全部是@Deprecatedpublic native void monitorEnter(Object var1);public native void monitorExit(Object var1);public native boolean tryMonitorEnter(Object var1);//異常拋出public native void throwException(Throwable var1);//CAS操作/*** 比較obj的offset處記憶體位置中的值和期望的值,如果相同則更新,此更新是不可中斷的* @param obj 需要更新的對象* @param offset obj中整型field的位移量* @param expect 希望field中存在的值* @param update 如果期望值expect與field的當前值相同,設定filed的值為這個新值* @return 如果field的值被更改返回true*/public final native boolean compareAndSwapObject(Object obj, long offset, Object expect, Object update);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);//擷取給定對象的指定類型值,支援volatile load語義public native Object getObjectVolatile(Object var1, long var2);public native void putObjectVolatile(Object var1, long var2, Object var4);public native int getIntVolatile(Object var1, long var2);//設定給定對象的int值,支援volatile load語義public native void putIntVolatile(Object var1, long var2, int var4);//....還有Boolean、Byte、Char、Short、Long、Float、Double的volatile層級的get\put....public native void putDoubleVolatile(Object var1, long var2, double var4);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);//LockSupport類的原語支援-掛起和喚醒某個線程public native void unpark(Object var1);public native void park(boolean var1, long var2);public native int getLoadAverage(double[] var1, int var2);//提供安全執行緒的add和set操作public final int getAndAddInt(Object var1, long var2, int var4) {int var5; do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5;}public final int getAndSetInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var4)); return var5;}//...還有Long和Object的安全執行緒的add和set操作...//柵欄支援public native void loadFence();public native void storeFence();public native void fullFence();
5. 禁用的Factory 方法
@CallerSensitivepublic static Unsafe getUnsafe() { Class var0 = Reflection.getCallerClass(); //該方法用於判斷調用者的類載入器是否是系統核心載入器(即Bootstrap載入器) if(!VM.isSystemDomainLoader(var0.getClassLoader())) { throw new SecurityException("Unsafe"); } else { return theUnsafe; }}
- 我們先顯性調用該Factory 方法查看一下調用結果
package concurrent;import sun.misc.Unsafe;public class UnsafeDemo { public static void main(String[] args) { Unsafe.getUnsafe(); }}-------------------//輸出:Exception in thread "main" java.lang.SecurityException: Unsafe at sun.misc.Unsafe.getUnsafe(Unsafe.java:90) at concurrent.UnsafeDemo.main(UnsafeDemo.java:7)//分析:可以發現直接調用的話會直接拋出安全異常,原因是類載入器是AppClassLoader而並非是BootstrapLoader
- 根據Java 類載入器的工作原理,應用程式的類由AppLoader載入,而系統核心類由BootstrapLoader載入
- 當一個類的類載入器為null時,說明它是由BootstrapLoader載入的,即此類是系統核心類(比如rt.jar包中的類)
- 當一個類無法被BootstrapLoader載入時,其類載入器通常為AppClassLoader,即是屬於自訂類
■ Unsafe 反射擷取
/** * 我們可以通過反射機制擷取Unsafe 的一個執行個體 */public static Unsafe getUnsafe(){ try { //通過反射擷取Unsafe的theUnsafe變數,即Unsafe執行個體對象 Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); //注意field是static屬性 //參見:private static final Unsafe theUnsafe; return (Unsafe) f.get(null); } catch (Exception e) { e.printStackTrace(); } return null;}