Java8中CAS的增強

pjp 9年前發布 | 11K 次閱讀 Java8 Java開發

幾天前,我偶然地將之前寫的用來測試AtomicInteger和synchronized的自增性能的代碼跑了一下,意外地發現AtomicInteger的性能比synchronized更好了,經過一番原因查找,有了如下發現:

在jdk1.7中,AtomicInteger的getAndIncrement是這樣的:

    public final int getAndIncrement() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return current;
        }
    }
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

而在jdk1.8中,是這樣的:

    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

可以看出,在jdk1.8中,直接使用了Unsafe的getAndAddInt方法,而在jdk1.7的Unsafe中,沒有此方法。(PS:為了找出原因,我反編譯了Unsafe,發現CAS的失敗重試就是在getAndAddInt方法里完成的,我用反射獲取到Unsafe實例,編寫了跟getAndAddInt相同的代碼,但測試結果卻跟jdk1.7的getAndIncrement一樣慢,不知道Unsafe里面究竟玩了什么黑魔法,還請高人不吝指點)

通過查看AtomicInteger的源碼可以發現,受影響的還有getAndAdd、addAndGet等大部分方法。

有了這次對CAS的增強,我們又多了一個使用非阻塞算法的理由。

最后給出測試代碼,需要注意的是,此測試方法簡單粗暴,compareAndSet的性能不如synchronized,并不能簡單地說synchronized就更好,兩者的使用方式是存在差異的,而且在實際使用中,還有業務處理,不可能有如此高的競爭強度,此對比僅作為一個參考,該測試能夠證明的是,AtomicInteger.getAndIncrement的性能有了大幅提升。

package performance;

import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.LockSupport;

public class AtomicTest { //測試規模,調用一次getAndIncreaseX視作提供一次業務服務,記錄提供TEST_SIZE次服務的耗時 private static final int TEST_SIZE = 100000000; //客戶線程數 private static final int THREAD_COUNT = 10; //使用CountDownLatch讓各線程同時開始 private CountDownLatch cdl = new CountDownLatch(THREAD_COUNT + 1);

private int n = 0;
private AtomicInteger ai = new AtomicInteger(0);
private long startTime;

public void init() {
    startTime = System.nanoTime();
}

/**
 * 使用AtomicInteger.getAndIncrement,測試結果為1.8比1.7有明顯性能提升
 * @return
 */
private final int getAndIncreaseA() {
    int result = ai.getAndIncrement();
    if (result == TEST_SIZE) {
        System.out.println(System.nanoTime() - startTime);
        System.exit(0);
    }
    return result;
}

/**
 * 使用synchronized來完成同步,測試結果為1.7和1.8幾乎無性能差別
 * @return
 */
private final int getAndIncreaseB() {
    int result;
    synchronized (this) {
        result = n++;
    }
    if (result == TEST_SIZE) {
        System.out.println(System.nanoTime() - startTime);
        System.exit(0);
    }
    return result;
}

/**
 * 使用AtomicInteger.compareAndSet在java代碼層面做失敗重試(與1.7的AtomicInteger.getAndIncrement的實現類似),
 * 測試結果為1.7和1.8幾乎無性能差別
 * @return
 */
private final int getAndIncreaseC() {
    int result;
    do {
        result = ai.get();
    } while (!ai.compareAndSet(result, result + 1));
    if (result == TEST_SIZE) {
        System.out.println(System.nanoTime() - startTime);
        System.exit(0);
    }
    return result;
}

public class MyTask implements Runnable {
    @Override
    public void run() {
        cdl.countDown();
        try {
            cdl.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        while (true)
            getAndIncreaseA();// getAndIncreaseB();
    }
}

public static void main(String[] args) throws InterruptedException {
    AtomicTest at = new AtomicTest();
    for (int n = 0; n < THREAD_COUNT; n++)
        new Thread(at.new MyTask()).start();
    System.out.println("start");
    at.init();
    at.cdl.countDown();
}

}</pre>

以下是在Intel(R) Core(TM) i7-4710HQ CPU @2.50GHz(四核八線程)下的測試結果(波動較小,所以每項只測試了四五次,取其中一個較中間的值):
jdk1.7
AtomicInteger.getAndIncrement 12,653,757,034
synchronized 4,146,813,462
AtomicInteger.compareAndSet 12,952,821,234

jdk1.8
AtomicInteger.getAndIncrement 2,159,486,620
synchronized 4,067,309,911
AtomicInteger.compareAndSet 12,893,188,541

原創文章,轉載請注明: 轉載自并發編程網 – ifeve.com

本文鏈接地址: Java8中CAS的增強

 本文由用戶 pjp 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!