從JDK源碼角度看併發的原子性如何保證

JDK源碼中,在研究AQS框架時,會發現不少地方都使用了CAS操做,在併發實現中CAS操做必須具有原子性,並且是硬件級別的原子性,Java被隔離在硬件之上,明顯力不從心,這時爲了能直接操做操做系統層面,確定要經過用C++編寫的native本地方法來擴展實現。JDK提供了一個類來知足CAS的要求,sun.misc.Unsafe,從名字上能夠大概知道它用於執行低級別、不安全的操做,AQS就是使用此類完成硬件級別的原子操做。html

Unsafe是一個很強大的類,它能夠分配內存、釋放內存、能夠定位對象某字段的位置、能夠修改對象的字段值、可使線程掛起、使線程恢復、可進行硬件級別原子的CAS操做等等,但平時咱們沒有這麼特殊的需求去使用它,並且必須在受信任代碼(通常由JVM指定)中調用此類,例如直接Unsafe unsafe = Unsafe.getUnsafe();獲取一個Unsafe實例是不會成功的,由於這個類的安全性很重要,設計者對其進行了以下判斷,它會檢測調用它的類是否由啓動類加載器Bootstrap ClassLoader(它的類加載器爲null)加載,由此保證此類只能由JVM指定的類使用。java

public static Unsafe getUnsafe() {  
   Class cc = sun.reflect.Reflection.getCallerClass(2);  
   if (cc.getClassLoader() != null)  
       throw new SecurityException("Unsafe");  
   return theUnsafe;  
}複製代碼

固然能夠經過反射繞過上面的限制,用下面的getUnsafeInstance方法能夠獲取Unsafe實例,這段代碼演示瞭如何獲取java對象的相對地址偏移量及使用Unsafe完成CAS操做,最終輸出的是flag字段的內存偏移量及CAS操做後的值。分別爲8和101。另外若是使用開發工具如Eclipse,可能會編譯通不過,只要把編譯錯誤提示關掉便可。安全

public class UnsafeTest {  
private int flag = 100;  
private static long offset;  
private static Unsafe unsafe = null;  
static{  
     try{  
          unsafe= getUnsafeInstance();  
          offset= unsafe.objectFieldOffset(UnsafeTest.class  
                   .getDeclaredField("flag"));  
     }catch (Exception e) {  
          e.printStackTrace();  
     }  
}  

public static void main(String[] args) throws Exception {  
     int expect = 100;  
     int update = 101;  
     UnsafeTest unsafeTest = 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;  
}  

private static Unsafe getUnsafeInstance() throws SecurityException,  
          NoSuchFieldException,IllegalArgumentException,  
          IllegalAccessException{  
     Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe");  
     theUnsafeInstance.setAccessible(true);  
     return (Unsafe)theUnsafeInstance.get(Unsafe.class);  
}  
}複製代碼

Unsafe類讓咱們明白了java是如何實現對操做系統操做的,通常咱們使用java是不須要在內存中處理java對象及內存地址位置的,但有的時候咱們確實須要知道java對象相關的地址,因而咱們使用Unsafe類,儘管java對其提供了足夠的安全管理。bash

Java語言的設計者們極力隱藏涉及底層操做系統的相關操做,但此節咱們本着對AQS框架實現的目的,不得不剖析了Unsafe類,由於AQS裏面便是使用Unsafe獲取對象字段的地址偏移量、相關原子操做來實現CAS操做的。併發

如下是廣告相關閱讀框架

========廣告時間========工具

鄙人的新書《Tomcat內核設計剖析》已經在京東銷售了,有須要的朋友能夠到 item.jd.com/12185360.ht… 進行預約。感謝各位朋友。post

爲何寫《Tomcat內核設計剖析》開發工具

=========================優化

相關閱讀:

從JDK源碼角度看Object

談談Java基礎數據類型

從JDK源碼角度看併發鎖的優化

從JDK源碼角度看線程的阻塞和喚醒

從JDK源碼角度看併發競爭的超時

從JDK源碼角度看java併發線程的中斷

從JDK源碼角度看Java併發的公平性

從JDK源碼角度看java併發的原子性如何保證

從JDK源碼角度看Byte

從JDK源碼角度看Boolean

從JDK源碼角度看Short

歡迎關注:

這裏寫圖片描述
這裏寫圖片描述
相關文章
相關標籤/搜索