JDK併發AQS系列(二)

超人汪小建發表於2019-02-26

原子性

在研究JDK中AQS時,會發現這個類很多地方都使用了CAS操作,在併發實現中CAS操作必須具備原子性,而且是硬體級別的原子性,java被隔離在硬體之上,明顯力不從心,這時為了能直接操作作業系統層面,肯定要通過用C++編寫的native本地方法來擴充套件實現。JDK提供了一個類來滿足CAS的要求,sun.misc.Unsafe,從名字上可以大概知道它用於執行低階別、不安全的操作,AQS就是使用此類完成硬體級別的原子操作。

Unsafe類

Unsafe是一個很強大的類,它可以分配記憶體、釋放記憶體、可以定位物件某欄位的位置、可以修改物件的欄位值、可以使執行緒掛起、使執行緒恢復、可進行硬體級別原子的CAS操作等等。

但平時我們沒有這麼特殊的需求去使用它,而且必須在受信任程式碼(一般由JVM指定)中呼叫此類,例如直接Unsafe unsafe = Unsafe.getUnsafe();獲取一個Unsafe例項是不會成功的,因為這個類的安全性很重要,設計者對其進行了如下判斷,它會檢測呼叫它的類是否由啟動類載入器Bootstrap ClassLoader(它的類載入器為null)載入,由此保證此類只能由JVM指定的類使用。判斷邏輯如下,

public static Unsafe getUnsafe() {

   Class cc = sun.reflect.Reflection.getCallerClass(2);

   if (cc.getClassLoader() != null)

       throw new SecurityException("Unsafe");

   return theUnsafe;

}
複製程式碼

獲取Unsafe

當然可以通過反射繞過上面的限制,用下面的getUnsafeInstance方法可以獲取Unsafe例項,這段程式碼演示瞭如何獲取java物件的相對地址偏移量及使用Unsafe完成CAS操作,最終輸出的是flag欄位的記憶體偏移量及CAS操作後的值。分別為8和101。另外如果使用開發工具如Eclipse,可能會編譯通不過,只要把編譯錯誤提示關掉即可。

public class UnsafeTest {

privateint flag = 100;
privatestatic long offset;
privatestatic Unsafe unsafe = null;

static{
     try{
          unsafe= getUnsafeInstance();
          offset= unsafe.objectFieldOffset(UnsafeTest.class.getDeclaredField("flag"));
     }catch (Exception e) {
          e.printStackTrace();
     }
}

publicstatic void main(String[] args) throws Exception {

     intexpect = 100;
     intupdate = 101;

     UnsafeTestunsafeTest = new UnsafeTest();
     System.out.println("unsafeTest物件的flag欄位的地址偏移量為:"+offset);
     unsafeTest.doSwap(offset,expect, update);
     System.out.println("CAS操作後的flag值為:" +unsafeTest.getFlag());

}

privateboolean doSwap(long offset, int expect, int update) {
     returnunsafe.compareAndSwapInt(this, offset, expect, update);
}

publicint getFlag() {
     returnflag;
}

privatestatic Unsafe getUnsafeInstance() throws SecurityException,NoSuchFieldException,IllegalArgumentException,IllegalAccessException{
     FieldtheUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe");
     theUnsafeInstance.setAccessible(true);
     return(Unsafe) theUnsafeInstance.get(Unsafe.class);
}

}
複製程式碼

Unsafe類讓我們明白了java是如何實現對作業系統操作的,一般我們使用java是不需要在記憶體中處理java物件及記憶體地址位置的,但有的時候我們確實需要知道java物件相關的地址,於是我們使用Unsafe類,儘管java對其提供了足夠的安全管理。

總結

Java語言的設計者們極力隱藏涉及底層作業系統的相關操作,但這裡我們為了探索AQS的實現,不得不剖析了Unsafe類,因為AQS裡面即是使用Unsafe獲取物件欄位的地址偏移量、相關原子操作來實現CAS操作的。

-------------推薦閱讀------------

我的開源專案彙總(機器&深度學習、NLP、網路IO、AIML、mysql協議、chatbot)

為什麼寫《Tomcat核心設計剖析》

我的2017文章彙總——機器學習篇

我的2017文章彙總——Java及中介軟體

我的2017文章彙總——深度學習篇

我的2017文章彙總——JDK原始碼篇

我的2017文章彙總——自然語言處理篇

我的2017文章彙總——Java併發篇


跟我交流,向我提問:

JDK併發AQS系列(二)

歡迎關注:

JDK併發AQS系列(二)

相關文章