一、複習
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類就是把一個共享變量分成爲了多個共享變量,那麼線程去競爭的時候,壓力會小一些,每個線程競爭到共享變量的可能性大大增加。
基本構成: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類進行填充,這樣按行加載內存機制就得到優化,提升性能。
詳細原理我們下次再說
三、源碼:
所在包:com.ruigege.AtomicOperationClass4 https://github.com/ruigege66/ConcurrentJava
CSDN:https://blog.csdn.net/weixin_44630050 博客園:https://www.cnblogs.com/ruigege0000/ 歡迎關注微信公衆號:傅里葉變換,個人賬號,僅用於技術交流