- 更快的原子類:LongAdder
大家對AtomicInteger的基本實現機制應該比較了解,它們是在一個死循環內,不斷嘗試修改目標值,知道修改成功,如果競爭不激烈,那么修改成功的概率就很高,否則,修改失敗的概率就很高,在大量修改失敗時,這些原子操作就會進行多次循環嘗試,因此性能就會受到影響
那么競爭激烈的時候,我們應該如何進一步提高系統性能呢?一種基本方案就是可以使用熱點分離,將競爭的數據進行分解.基於這個思路,打擊應該可以想到一種對傳統AtomicInteger等原子類的改進方法,雖然在CAS操作中沒有鎖,但是像減少鎖粒度這種分離熱點的思路依然可以使用,一種可行的方案就是仿造ConcurrengHashMap,將熱點數據分離,比如,可以將AtomicInteger的內部核心數據value分離成一個數組,每個線程訪問時,通過哈希等算法映射到其中一個數字進行計數,而最終的計數結果,則為這個數組的求和累加,其中,熱點數據value被分離成多個單元cell,每個cell獨自維護內部的值,當前對象的實際值由所有的cell累計合成,這樣,熱點就進行了有效的分離,提高了並行度,LongAdder正是使用了這種思想.
- 測試LongAdder,原子類以及同步鎖性能測試
public class LongAdderDemo { private static final int MAX_THREADS = 3; private static final int TASK_COUNT = 3; private static final int TARGET_COUNT = 10000000; private AtomicLong acount = new AtomicLong(0L); private LongAdder lacount = new LongAdder(); private long count = 0; private static CountDownLatch cdlsync = new CountDownLatch(TASK_COUNT); private static CountDownLatch cdlatomic = new CountDownLatch(TASK_COUNT); private static CountDownLatch cdladdr = new CountDownLatch(TASK_COUNT); protected synchronized long inc() { return ++count; } protected synchronized long getCount() { return count; } public class SyncThread implements Runnable { protected String name; protected long starttime; LongAdderDemo out; public SyncThread(long starttime, LongAdderDemo out) { this.starttime = starttime; this.out = out; } @Override public void run() { long v = out.getCount(); while (v < TARGET_COUNT) { v = out.inc(); } long endtime = System.currentTimeMillis(); System.out.println("SyncThread spend:" + (endtime - starttime) + "ms" + " v" + v); cdlsync.countDown(); } } public void testSync() throws InterruptedException { ExecutorService exe = Executors.newFixedThreadPool(MAX_THREADS); long starttime = System.currentTimeMillis(); SyncThread sync = new SyncThread(starttime, this); for (int i = 0; i < TASK_COUNT; i++) { exe.submit(sync); } cdlsync.await(); exe.shutdown(); } public class AtomicThread implements Runnable { protected String name; protected long starttime; public AtomicThread(long starttime) { this.starttime = starttime; } @Override public void run() { long v = acount.get(); while (v < TARGET_COUNT) { v = acount.incrementAndGet(); } long endtime = System.currentTimeMillis(); System.out.println("AtomicThread spend:" + (endtime - starttime) + "ms" + " v" + v); cdlatomic.countDown(); } } public void testAtomic() throws InterruptedException { ExecutorService exe = Executors.newFixedThreadPool(MAX_THREADS); long starttime = System.currentTimeMillis(); AtomicThread atomic = new AtomicThread(starttime); for (int i = 0; i < TASK_COUNT; i++) { exe.submit(atomic); } cdlatomic.await(); exe.shutdown(); } public class LongAdderThread implements Runnable { protected String name; protected long starttime; public LongAdderThread(long starttime) { this.starttime = starttime; } @Override public void run() { long v = lacount.sum(); while (v < TARGET_COUNT) { lacount.increment(); v = lacount.sum(); } long endtime = System.currentTimeMillis(); System.out.println("LongAdderThread spend:" + (endtime - starttime) + "ms" + " v" + v); cdladdr.countDown(); } } public void testLongAdder() throws InterruptedException { ExecutorService exe = Executors.newFixedThreadPool(MAX_THREADS); long starttime = System.currentTimeMillis(); LongAdderThread atomic = new LongAdderThread(starttime); for (int i = 0; i < TASK_COUNT; i++) { exe.submit(atomic); } cdladdr.await(); exe.shutdown(); } public static void main(String[] args) throws InterruptedException { LongAdderDemo demo = new LongAdderDemo(); demo.testSync(); demo.testAtomic(); demo.testLongAdder(); } }
