JDK 8中的LondAddr 與synchronized和AtomicLong併發性能對比實驗

目錄

 

前文

正文

0. 測試環境

1. Synchronized 測試

2. AtomicLong測試

3. LongAdder測試

總結


前文

       多線程之間常見同步方式 :Synchronized / Lock / volitial + CAS 等,在不同的併發量和不同併發場景下性能不盡相同,尤其是Synchronized在不斷經過優化之後性能得到了提升,而且無鎖化CAS編程也成爲了一些框架提升性能的手段,在JDK 8中,處理已有的原子類之外,添加了LongAdder和LongAccumulator原子類,除去應用場景不說,性能要比原始的原子類性能要好。

      LongAdder的實現原理是將原本的value,切分成了多個數組,每個線程單獨操作數組中自己對應的值,在獲取LongAdder的結果的時候,需要對數組中所有的值進行求和sum操作,LongAccumulator是LongAdder的功能增強版本,原理類似。

   

 

正文

      以下分別對Synchronized、AtomicLong和LongAdder在多線程下的性能進行簡單測試。

0. 測試環境

     測試環境就不詳細給出了,個人mac pro電腦,編譯器:idea ,jdk 8……

1. Synchronized 測試

所需代碼:
 

private static final int MAX_THREADS = 3;				   //線程數
private static final int TARGET_COUNT = 10000000;		   //目標總數


//用於統計完成子線程數目
static CountDownLatch cdlsync = new CountDownLatch(MAX_THREADS);


//所需要同步的變量
private long count = 0;


//同步的方法
protected synchronized long getCount(){
        return count;
}
protected synchronized long inc(){
    //如果不在這裏進行越界判斷,會出現超出TARGET_COUNT的情況
    if(count<TARGET_COUNT){
            return ++count;
    }
    return count;
}



//多線程類
public class SynThread implements  Runnable{
        private CountDownLatch cdlsync = null;
        private long start_time;
        private MuilThreadCompare compare=null;
        public SynThread(MuilThreadCompare o,CountDownLatch cdlsync,long start_time){
            this.compare = o;
            this.cdlsync = cdlsync;
            this.start_time = start_time;
        }
        @Override
        public void run() {
            long re = compare.getCount();
            while(re<TARGET_COUNT){
                 re = compare.inc();
            }
            long endtime=System.currentTimeMillis();
            System.out.println("SyncThread spend:"+(endtime-start_time)+"ms"+" v="+re);
            cdlsync.countDown();
        }
}



//測試方法
public void testSyn() throws InterruptedException{
        ExecutorService pool = Executors.newFixedThreadPool(MAX_THREADS);
        long starttime=System.currentTimeMillis();
        for (int i = 0; i < MAX_THREADS; i++) {
            SynThread thread = new SynThread(this,cdlsync,starttime);
            pool.submit(thread);
        }
        cdlsync.await();
        pool.shutdown();
}


    

結果輸入如下所示,使用3個線程,時間穩定在320ms左右,未出現結果越界情況:

2. AtomicLong測試

所需代碼如下:

static CountDownLatch cdlatomic=new CountDownLatch(MAX_THREADS);

//原子類
private AtomicLong acount =new AtomicLong(0L);


//線程類
public class AtomicLongThread implements Runnable{
        private long start_time;
        private AtomicLong atomic = null;
        public AtomicLongThread(AtomicLong atomic,long start_time){
            this.atomic = atomic;
            this.start_time = start_time;

        }
        @Override
        public void run() {
            long temp_count = atomic.get();
            while(temp_count<TARGET_COUNT){
                // 不要用incrementAndGet 方法,否則會出現結果越界情況
                //temp_count = atomic.incrementAndGet();
                atomic.compareAndSet(temp_count,temp_count+1);
                temp_count++;
            }
            long end_time=System.currentTimeMillis();
            System.out.println("AtomicThread spend:"+(end_time-start_time)+"ms"+" v="+temp_count);
            cdlatomic.countDown();
        }
}



//測試方法:
public void testAtomicLong() throws InterruptedException{
        ExecutorService pool = Executors.newFixedThreadPool(MAX_THREADS);
        long starttime=System.currentTimeMillis();

        for (int i = 0; i < MAX_THREADS; i++) {
            AtomicLongThread thread = new AtomicLongThread(acount,starttime);
            pool.submit(thread);
        }
        cdlatomic.await();
        pool.shutdown();
}

結果如下圖所示,穩定在370ms左右,未出現結果越界情況,一開始的時候使用incrementAndGet API更新值會出現結果越界的情況:

 

3. LongAdder測試

所需代碼:
 

static CountDownLatch cdladdr=new CountDownLatch(MAX_THREADS);


//新原子類
private LongAdder lacount=new LongAdder();


//線程類
public class LongAdderThread implements Runnable{
        private long start_time;
        private LongAdder longAdder = null;
        public  LongAdderThread(long start_time,LongAdder longAdder){
            this.start_time = start_time;
            this.longAdder = longAdder;
        }
        @Override
        public void run() {
            long v = longAdder.sum();
            while(v<TARGET_COUNT){
                longAdder.increment();
                v=longAdder.sum();
            }
            long end_time=System.currentTimeMillis();
            System.out.println("LongAdder spend:"+(end_time-start_time)+"ms"+" v="+v);
            cdladdr.countDown();
        }
}


//測試方法
public void testLongAdder() throws InterruptedException{
        ExecutorService pool = Executors.newFixedThreadPool(MAX_THREADS);
        long starttime=System.currentTimeMillis();

        for (int i = 0; i < MAX_THREADS; i++) {
            LongAdderThread thread = new LongAdderThread(starttime,lacount);
            pool.submit(thread);
        }
        cdlatomic.await();
        pool.shutdown();
}

 

結果如下圖所示,時間穩定在180ms左右,但是存在結果值越界的情況,應爲:

在更新值的時候,先獲取的value的值,然後原子增加,期間可能插入其他線程的操作,導致結果越界。

總結

       測試的線程數目不多,3個線程,在低線程數目的情況下,新的原子類表現要比普通原子類和Synchronized性能優良,類推多線程數更多的情況下,LongAdder表現會更好,增加到10線程數的時候,LongAdder:200ms  AtomicLong:1330ms Synchronized:384ms ,繼續增加線程數差距會越大。

      證明LongAdder性能優良的同時,比較意外的是Synchronized性能同樣很好,甚至超過了普通的原子類,限制於機器性能,沒有測試測試線程數更多的情況,看來低線程的時候,Synchronized要比自旋CAS要好。

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