Unsafe 是位於 sun.misc 包下的一個類。Unsafe 提供的 API 大體可分爲內存操做、CAS、Class 相關、對象操做、線程調度、系統信息獲取、內存屏障、數組操做等幾類。因爲併發相關的源碼不少用到了 CAS,好比 java.util.concurrent.atomic 相關類、AQS、CurrentHashMap 等相關類。因此本文主要講 Unsafe 中 CAS 的實現。筆者源碼環境爲 OpenJDK8
。java
主要相關源碼算法
/** * 參數說明 * @param o 包含要修改field的對象 * @param offset 對象中某個參數field的偏移量,該偏移量不會改變 * @param expected 指望該偏移量對應的field值 * @param x 更新值 * @return true|false */ public final native boolean compareAndSwapObject(Object o, long offset, Object expected, Object x); public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x); public final native boolean compareAndSwapLong(Object o, long offset, long expected, long x);
CAS 是實現併發算法時經常使用到的一種技術。CAS 操做包含三個操做數——內存位置、預期原值及新值。執行 CAS 操做的時候,將內存位置的值與預期原值比較,若是相匹配,那麼處理器會自動將該位置值更新爲新值,不然,處理器不作任何操做。咱們都知道,CAS 是一條 CPU 的 原子指令
(cmpxchg 指令),不會形成所謂的數據不一致問題,Unsafe 提供的 CAS 方法(如 compareAndSwapXXX)底層實現即爲 CPU 指令 cmpxchg。bootstrap
說明:對象的基地址 baseAddress+valueOffset 獲得 value 的內存地址 valueAddress
首先看下 Unsafe 的單例實現數組
private static final Unsafe theUnsafe = new Unsafe(); // 註解代表須要引導類加載器 @CallerSensitive public static Unsafe getUnsafe() { Class<?> caller = Reflection.getCallerClass(); // 僅在引導類加載器`BootstrapClassLoader`加載時才合法 if (!VM.isSystemDomainLoader(caller.getClassLoader())) throw new SecurityException("Unsafe"); return theUnsafe; }
那如若想使用這個類,該如何獲取其實例?有以下兩個可行方案。安全
其一,從 getUnsafe
方法的使用限制條件出發,經過 Java 命令行命令 -Xbootclasspath/a
把調用 Unsafe 相關方法的類 A 所在 jar 包路徑追加到默認的 bootstrap 路徑中,使得 A 被引導類加載器加載,從而經過 Unsafe.getUnsafe
方法安全的獲取 Unsafe 實例。併發
java -Xbootclasspath/a: ${path} // 其中path爲調用Unsafe相關方法的類所在jar包路徑
其二,經過反射獲取單例對象 theUnsafe。測試
@Slf4j public class UnsafeTest { private static Unsafe reflectGetUnsafe() { try { Field field = Unsafe.class.getDeclaredField("theUnsafe"); field.setAccessible(true); return (Unsafe) field.get(null); } catch (Exception e) { log.error(e.getMessage(), e); return null; } } public static void main(String[] args) { Unsafe unsafe = UnsafeTest.reflectGetUnsafe(); } }
@Getter@Setter public class User { private String name; private int age; }
@Slf4j public class UnsafeTest { private static Unsafe reflectGetUnsafe() { try { Field field = Unsafe.class.getDeclaredField("theUnsafe"); field.setAccessible(true); return (Unsafe) field.get(null); } catch (Exception e) { log.error(e.getMessage(), e); return null; } } public static void main(String[] args) throws Exception{ Unsafe unsafe = UnsafeTest.reflectGetUnsafe(); // allocateInstance: 對象操做。繞過構造方法、初始化代碼來建立對象 User user = (User)unsafe.allocateInstance(User.class); user.setName("admin"); user.setAge(17); Field name = User.class.getDeclaredField("name"); Field age = User.class.getDeclaredField("age"); // objectFieldOffset: 返回對象成員屬性在內存地址相對於此對象的內存地址的偏移量 long nameOffset = unsafe.objectFieldOffset(name); long ageOffset = unsafe.objectFieldOffset(age); System.out.println("name內存偏移地址:" + nameOffset); System.out.println("age 內存偏移地址:" + ageOffset); System.out.println("---------------------"); // CAS操做 int currentValue = unsafe.getIntVolatile(user, ageOffset); System.out.println("age內存當前值:" + currentValue); boolean casAge = unsafe.compareAndSwapInt(user, ageOffset, 17, 18); System.out.println("age進行CAS更新成功:" + casAge); System.out.println("age更新後的值:" + user.getAge()); System.out.println("---------------------"); // volatile修飾,保證可見性、有序性 unsafe.putObjectVolatile(user, nameOffset, "test"); System.out.println("name更新後的值:" + unsafe.getObjectVolatile(user, nameOffset)); } }
結果輸出atom
name內存偏移地址:16 age 內存偏移地址:12 --------------------- age內存當前值:17 age進行CAS更新成功:true age更新後的值:18 --------------------- name更新後的值:test
Unsafe 中 CAS 操做是原子性的,因此在秒殺、庫存扣減中也能夠使用 Unsafe 來扣減庫存。spa
本文對 Java 中的 sun.misc.Unsafe 的用法及應用場景進行了基本介紹,僅作後續源碼閱讀的鋪墊。到此,本篇文章就寫完了,感謝你們的閱讀!若是您以爲對您有幫助,請關注公衆號【當我趕上你】。命令行