Java高併發15-AtomicLong等原子類源碼解析

一、複習

  • Random類以及ThreadLocalRandom類源碼解析

二、Java併發包中一些原子操作類的源碼解析

1.常見類

  • 例如AtomicLong,AtomicInteger,AtomicCharacter.....等這些操作類其內部實現都是通過CAS非阻塞算法來實現, 因此我們只要弄懂一個其他就基本知曉了,因此本文講解AtomicLong類的實現原理
  • AtomicLong還有LongAdder,LongAccumulator等類是對AtomicLong特性的增強,我們後面再說

2.AtomicLong類

  • 優點:使用CAS非阻塞算法實現併發,比使用synchronized等鎖機制的非阻塞算法,併發性要好很多。
  • 下面來看一下它的源碼,注意看註釋基本都解釋清楚了
package com.ruigege.AtomicOperationClass4;

import java.util.concurrent.atomic.AtomicLong;

import jdk.internal.misc.Unsafe;

import java.lang.Number;

public class AtomicLongTest extends Number implements java.io.Serializable{
 private static final long serialVersionUID = 1927816293512124184L;
 
 private static final Unsafe unsafe = Unsafe.getUnsafe();
 
 private volatile long value;//用於放實際值的變量
 
 private final long valueOffset;//value的偏移量
 
 static {
  try {
   valueOffset = unsafe.objectFieldOffset(AtomicLong.class.getDeclaredField(value));
   
  }catch(Exception e) {
   throw new Error(e);
  }
 }
 
 public AtomicLongTest(long initialValue) {
  value = initialValue;
 }
 
 //下面的語句是用於判斷JVM是否支持Long類型的CAS
 static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();
 private static native boolean VMSupportCS8();
 
 /**
  * 該方法使用CAS算法用於累加
  * @return
  */

 public final long incrementAndGet() {
  return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
 }
 /**
  * 我們來一起回顧一下Unsafe類中的成員方法getAndAddLong方法
  * public final long getAndAddLong(Object obj,long offset,long addValue){
  *     long l;
  *     do{
  *         l = getLongvolatie(obj,offset);//獲取obj對象偏移量爲offset的值
  *     }(!compareAndSwapLong(obj,offset,l,l+addValue));如與預期不相同,那麼我們不斷地獲取l的值,如果
  *     //相同了,說明這個時候可以+addValue將得到的值賦值給l就可以了
  *     return l;最後返回的是+addValue之前的值
  *     //我理解的CAS算法,其實它的本質就是不斷循環獲取一致的值,再進行更新。
  * }
  */

 
 /**
  * 該方法使用CAS用於累減
  * @return
  */

 public final long decrementAndGet() {
  return unsafe.getAndAddLong(this,valueOffset,-1L) -1L;
 }
 
 
 /**
  * 下面這兩個方法,可以和上面的兩個方法作對比,都是累加,但是上面返回是累加之後的值,下面是返回累加之前的值
  * @return
  */

 public final long getAndIncrement() {
  return unsafe.getAndAddLong(this, valueOffset, 1L);
 }
 
 public final long getAndDecrement() {
  return unsafe.getAndAddLong(this,valueOffset,-1L);
 }
 
 /**
  * 下面這個方法類似於Unsafe中的compareAndSwapLong方法
  * @param expect
  * @param update
  * @return
  */

 public final boolean compareAndSet(long expect,long update) {
  return unsafe.compareAndSwapLong(this,valueOffset,expect,update);
 }
}
  • 上面的代碼是JDK8中的代碼,我們可以拿出一個方法getAndIncrement,來對比再JDK7中的實現源碼
public final int getAndIncrement(){
 while(true){
  long temp = get();//這一行就是或者value的值
  long newTemp = temp + 1;
  if(compareAndSet(temp,newTemp){
  //這裏面的判斷是使用了該類裏面測成員方法
   return temp;
  }
 }
}
  • 這個循環的意義其實就是一個CAS操作,我們不斷循環獲取value的值, 然後一致了,就開始設置,不一致的時候,就不斷的開始獲取,直到一致。JDK8其實不過是使用了Unsafe類作爲工具類,來實現CAS操作。
  • 在沒有原子類的時候,我們使用鎖synchronize機制來保證一致性,會大大降低併發效率;使用原子類,提高了效率,但是在大規模的併發下,這個類還是存在效率低的問題,因此引出今日主角

3.LongAdder類

  • atomicLong的困境以及如果優化:AtomicLong類的問題在於,在高併發的情況下,所有的線程都會區競爭同一個共享變量,並且根據CAS機制,最後只有一個線程能夠更新成功,這就導致了,其他線程爲了獲取共享變量,會進入到無限循環的自旋中;引入LongAdder類就是把一個共享變量分成爲了多個共享變量,那麼線程去競爭的時候,壓力會小一些,每個線程競爭到共享變量的可能性大大增加。 15.1

  • 基本構成:LongAdder內部是由一個Cell數組(需要初始化時纔會創建數組,沒有初始化時爲null)和一個base變量,當創建該類實例以後,共享變量會變成多個Cell對象(共享變量被Cell類封裝了),然後在高併發下,線程可以競爭的共享變量(Cell實例)就變多了,並且一個線程如果沒有獲取到Cell實例,並不會進入到自旋狀態下,而是去競爭其他Cell;LongAdder實例的值,就是個各個Cell值累加,在加上base變量的值。該類其內部數組是一個延遲初始化原子性更新數組,屬於惰性加載;

  • 當一開始判斷Cell數組時null時,所有累加操作都是在base變量上的,保持Cell數組的大小是2的N次方,在初始化數組的時候,大小爲2,裏面的元素是Cell實例,這個是一個Atomic改進類,用於解決僞共享問題

  • 變量加載時的提升原理:對於大多數孤立的多個資源子操作進行字節填充是浪費,但是 LongAdder實例的數組可以是內存連續,因此我們需要添加個@sun.misc.Contended註解對Cell類進行填充,這樣按行加載內存機制就得到優化,提升性能。

  • 詳細原理我們下次再說

三、源碼:

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