天天看點

并發程式設計--計數器不同實作方案性能對比【synchronized、LongAdder、LongAccumulator、AtomicLong】1. 計數器實作2. 計數器不同的實作方案

在并發程式設計時,經常需要進行計數,如統計處理的記錄條數、成功處理的條數、失敗處理的條數等,本文針對synchronized、LongAdder、LongAccumulator、AtomicLong四種方案實作的計數器進行性能對比,并給出使用建議。

1. 計數器實作

1.1. 實作方案及測試結果

分别使用synchronized、AtomicLong、LongAdder、LongAccumulator方案,實作計數器,實作代碼參見下面的代碼。

性能測試結果詳見下面的輸出結果,LongAdder和LongAccumulator性能相當,synchronized性能最差,AtomicLong的耗時約為synchronized一半,LongAdder和LongAccumulator的耗時約為前兩個的幾十分之一。

1.2. 結果分析

AtomicLong是JDK1.5開始出現的,裡面主要使用了一個long類型的value作為成員變量,然後使用循環的CAS操作去操作value的值,并發量比較大的情況下,CAS操作失敗的機率較高,内部失敗了會重試,導緻耗時可能會增加。

LongAdder是JDK1.8開始出現的,所提供的API基本上可以替換掉原先的AtomicLong。LongAdder在并發量比較大的情況下,操作資料的時候,相當于把這個數字分成了很多份數字,然後交給多個人去管控,每個管控者負責保證部分數字在多線程情況下操作的正确性。當多線程通路的時,通過hash算法映射到具體管控者去操作資料,最後再彙總所有的管控者的資料,得到最終結果。相當于降低了并發情況下鎖的粒度,是以效率比較高,看一下下面的圖,友善了解:

并發程式設計--計數器不同實作方案性能對比【synchronized、LongAdder、LongAccumulator、AtomicLong】1. 計數器實作2. 計數器不同的實作方案

LongAccumulator是LongAdder的功能增強版。LongAdder的API隻有對數值的加減,而LongAccumulator提供了自定義的函數操作,其構造函數如下:

/**
     * Creates a new instance using the given accumulator function
     * and identity element.
     * @param accumulatorFunction a side-effect-free function of two arguments
     * @param identity identity (initial value) for the accumulator function
     */
    public LongAccumulator(LongBinaryOperator accumulatorFunction,
                           long identity) {
        this.function = accumulatorFunction;
        base = this.identity = identity;
    }
           

LongAdder、LongAccumulator全面超越同步鎖及AtomicLong的方式,建議在使用AtomicLong的地方可以直接替換為LongAdder、LongAccumulator,吞吐量更高一些

2. 計數器不同的實作方案

模拟50個線程,每個線程對計數器遞增100萬次,最終結果應該是5000萬。

分别使用synchronized、LongAdder、LongAccumulator、AtomicLong四種實作方案,并進行性能測試。

2.1 實作代碼

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;

public class CounterTest {
    private static ICounter counter;

    public static void main(String[] args) throws Exception {
        for (int j = 1; j < 5; j++) {
            counter = CounterFactory.getCounter(j);
            System.out.println(String.format("基于%s進行計數", counter.getClass().getSimpleName()));
            for (int i = 0; i < 10; i++) {
                counter.reset();
                count(counter);
            }
        }
    }

    private static void count(ICounter counter) throws InterruptedException {
        long t1 = System.currentTimeMillis();
        int threadCount = 50;
        CountDownLatch countDownLatch = new CountDownLatch(threadCount);
        for (int i = 0; i < threadCount; i++) {
            new Thread(() -> {
                try {
                    for (int j = 0; j < 1000000; j++) {
                        counter.incr();
                    }
                } finally {
                    countDownLatch.countDown();
                }
            }).start();
        }
        countDownLatch.await();
        long t2 = System.currentTimeMillis();
        System.out.println(String.format("結果:%s,耗時(ms):%s", counter.get(), (t2 - t1)));
    }
}

interface ICounter {
    public void reset();

    public void incr();

    public long get();
}

class SynchronizedCounter implements ICounter {
    private Integer count = 0;
    private Object lock = new Object();

    @Override
    public void reset() {
        count = 0;
    }

    @Override
    public void incr() {
        synchronized (lock) {
            count++;
        }
    }

    @Override
    public long get() {
        return count;
    }
}

class AtomicLongCounter implements ICounter {
    private AtomicLong count = new AtomicLong(0);

    @Override
    public void reset() {
        count.set(0);
    }

    @Override
    public void incr() {
        count.incrementAndGet();
    }

    @Override
    public long get() {
        return count.get();
    }
}

class LongAdderCounter implements ICounter {
    private LongAdder count = new LongAdder();

    @Override
    public void reset() {
        count.reset();
    }

    @Override
    public void incr() {
        count.increment();
    }

    @Override
    public long get() {
        return count.sum();
    }
}

class LongAccumulatorCounter implements ICounter {
    private LongAccumulator count = new LongAccumulator((x, y) -> x + y, 0L);

    @Override
    public void reset() {
        count.reset();
    }

    @Override
    public void incr() {
        count.accumulate(1);
    }

    @Override
    public long get() {
        return count.longValue();
    }
}

class CounterFactory {
    public static ICounter getCounter(int counterType) throws Exception {
        switch (counterType) {
            case 1:
                return new SynchronizedCounter();
            case 2:
                return new AtomicLongCounter();
            case 3:
                return new LongAdderCounter();
            case 4:
                return new LongAccumulatorCounter();
        }
        throw new Exception("參數錯誤");
    }
}
           

2.2. 輸出結果

基于SynchronizedCounter進行計數

結果:50000000,耗時(ms):1451

結果:50000000,耗時(ms):1452

結果:50000000,耗時(ms):1397

結果:50000000,耗時(ms):1406

結果:50000000,耗時(ms):1408

結果:50000000,耗時(ms):1411

結果:50000000,耗時(ms):1422

結果:50000000,耗時(ms):1415

結果:50000000,耗時(ms):1434

結果:50000000,耗時(ms):1430

基于AtomicLongCounter進行計數

結果:50000000,耗時(ms):777

結果:50000000,耗時(ms):752

結果:50000000,耗時(ms):756

結果:50000000,耗時(ms):768

結果:50000000,耗時(ms):757

結果:50000000,耗時(ms):761

結果:50000000,耗時(ms):769

結果:50000000,耗時(ms):784

結果:50000000,耗時(ms):776

結果:50000000,耗時(ms):772

基于LongAdderCounter進行計數

結果:50000000,耗時(ms):60

結果:50000000,耗時(ms):53

結果:50000000,耗時(ms):54

結果:50000000,耗時(ms):52

結果:50000000,耗時(ms):55

結果:50000000,耗時(ms):51

結果:50000000,耗時(ms):53

結果:50000000,耗時(ms):55

結果:50000000,耗時(ms):51

結果:50000000,耗時(ms):51

基于LongAccumulatorCounter進行計數

結果:50000000,耗時(ms):55

結果:50000000,耗時(ms):51

結果:50000000,耗時(ms):52

結果:50000000,耗時(ms):51

結果:50000000,耗時(ms):52

結果:50000000,耗時(ms):52

結果:50000000,耗時(ms):51

結果:50000000,耗時(ms):51

結果:50000000,耗時(ms):51

結果:50000000,耗時(ms):50