Java高併發9-CAS操作與Unsafe類詳解

一、複習

  • 計算機內存模型,synchronized和volatile關鍵字簡介

二、兩者對比

  • sychronized和volatile都解決了內存可見性問題
  • 不同點:
  • (1)前者是獨佔鎖,並且存在者上下文切換的開銷以及線程重新調度的開銷;後者是非阻塞算法,不會造成上下文切換的開銷。
  • (2)前者可以保證操作的原子性,但是後者不能保證操作的原子性。

三、在什麼情況下才會使用volatile

  • 寫入變量是不依賴當前值的,如果是依賴當前值的話,由於獲取-計算-寫入,三者不是原子性操作,而volatile是保證原子性操作的。
  • 變量沒有加鎖的時候,如果變量加鎖了,是可以保證內存的可見性的因此不需要再使用volatile

四、Java中的原子性操作

  • 原子性操作通俗的來講就是一組操作,要麼都執行成功,要麼都執行失敗,不存在執行部分成功的情況
  • 使用synchronized關鍵字既可以保證操作的原子性又可以保證內存的可見性,volatile只能保證內存的可見性,但是不能保證操作的原子性;synchronized固然好,但在高併發的情況下,由於它是一種獨佔鎖,因此會引起性能低下的問題。

五、Java中的CAS操作

  • 定義:CAS(compare and swap)比較並交換,這是JDK提供的一種非阻塞算法,它通過硬件保證了比較-更新的原子性問題。JDK中的Unsafe類提供了一系列的compareAndSwap*方法,下面以compareAndSwapLong爲例進行講解
  • boolean compare(Object obj,long offset,long expect,long update)
  • 先分別解釋一下各個參數,obj是一個對象的引用(也就是對象存儲的地址),offset是相對於前面地址的偏移量,expect是一個預想的值,update代表如果和預想的值一樣,那麼就是使用update這個值來代替,並且返回true,否則返回false
  • 這是處理器提供的一種原子性指令

六、ABA問題

  • 描述:線程1獲取變量x的值爲A,然後嘗試修改爲B,但是此時如果有另一個線程修改了x的值爲B,同時又修改成了A,那麼線程2的這個A和線程1之前的A就不是同一個A了
  • 產生原因:環形依賴,變量的值從A到B,然後又從B到A,這樣只能一個方向輪轉,如果是從A到B,然後從B到C就不會出現這種情況。
  • 解決方式:JDK中的AtomicStampedReferece給每個變量一個時間戳,從而避免了ABA問題

七、Unsafe類

  • 在JDK中的rt.jar包中有許多方法都是native的,這是一種硬件級別的操作,使用JNI來調用C++底層函數來操作。

1.long objectFieldOffset(Field field)

  • 釋義:獲取某個對象的中的某個域值所在對象的中的內存偏移量
try{
 long value = Unsafe.objectFieldOffset(AutomicLong.class.getDeclaredField("value"));
}catch(Exception e){
 e.printStackTrace();
}

2.int arrayBaseOffset(Class arrayClass)

  • 釋義:獲取數組中的第一個元素地址

3.int arrayIndexOffset(Class arrayClass)

  • 釋義:獲取數組中第一個元素的字節大小

4.boolean compareAndSwapLong(Object obj,long offset,long expect,long update)

  • 可以見上文

八、源碼:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章