Java中對AtomicInteger和int值在多線程下遞增操作的測試
Java針對多線程下的數(shù)值安全計(jì)數(shù)器設(shè)計(jì)了一些類,這些類叫做原子類,其中一部分如下:
java.util.concurrent.atomic.AtomicBoolean; java.util.concurrent.atomic.AtomicInteger; java.util.concurrent.atomic.AtomicLong; java.util.concurrent.atomic.AtomicReference;
下面是一個(gè)對比 AtomicInteger 與 普通 int 值在多線程下的遞增測試,使用的是 junit4;
完整代碼:
package test.java; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Assert; import org.junit.Before; import org.junit.Test; /** * 測試AtomicInteger與普通int值在多線程下的遞增操作 */ public class TestAtomic { // 原子Integer遞增對象 public static AtomicInteger counter_integer;// = new AtomicInteger(0); // 一個(gè)int類型的變量 public static int count_int = 0; @Before public void setUp() { // 所有測試開始之前執(zhí)行初始設(shè)置工作 counter_integer = new AtomicInteger(0); } @Test public void testAtomic() throws InterruptedException { // 創(chuàng)建的線程數(shù)量 int threadCount = 100; // 其他附屬線程內(nèi)部循環(huán)多少次 int loopCount = 10000600; // 控制附屬線程的輔助對象;(其他await的線程先等著主線程喊開始) CountDownLatch latch_1 = new CountDownLatch(1); // 控制主線程的輔助對象;(主線程等著所有附屬線程都運(yùn)行完畢再繼續(xù)) CountDownLatch latch_n = new CountDownLatch(threadCount); // 創(chuàng)建并啟動(dòng)其他附屬線程 for (int i = 0; i < threadCount; i++) { Thread thread = new AtomicIntegerThread(latch_1, latch_n, loopCount); thread.start(); } long startNano = System.nanoTime(); // 讓其他等待的線程統(tǒng)一開始 latch_1.countDown(); // 等待其他線程執(zhí)行完 latch_n.await(); // long endNano = System.nanoTime(); int sum = counter_integer.get(); // Assert.assertEquals("sum 不等于 threadCount * loopCount,測試失敗", sum, threadCount * loopCount); System.out.println("--------testAtomic(); 預(yù)期兩者相等------------"); System.out.println("耗時(shí): " + ((endNano - startNano) / (1000 * 1000)) + "ms"); System.out.println("threadCount = " + (threadCount) + ";"); System.out.println("loopCount = " + (loopCount) + ";"); System.out.println("sum = " + (sum) + ";"); } @Test public void testIntAdd() throws InterruptedException { // 創(chuàng)建的線程數(shù)量 int threadCount = 100; // 其他附屬線程內(nèi)部循環(huán)多少次 int loopCount = 10000600; // 控制附屬線程的輔助對象;(其他await的線程先等著主線程喊開始) CountDownLatch latch_1 = new CountDownLatch(1); // 控制主線程的輔助對象;(主線程等著所有附屬線程都運(yùn)行完畢再繼續(xù)) CountDownLatch latch_n = new CountDownLatch(threadCount); // 創(chuàng)建并啟動(dòng)其他附屬線程 for (int i = 0; i < threadCount; i++) { Thread thread = new IntegerThread(latch_1, latch_n, loopCount); thread.start(); } long startNano = System.nanoTime(); // 讓其他等待的線程統(tǒng)一開始 latch_1.countDown(); // 等待其他線程執(zhí)行完 latch_n.await(); // long endNano = System.nanoTime(); int sum = count_int; // Assert.assertNotEquals( "sum 等于 threadCount * loopCount,testIntAdd()測試失敗", sum, threadCount * loopCount); System.out.println("-------testIntAdd(); 預(yù)期兩者不相等---------"); System.out.println("耗時(shí): " + ((endNano - startNano) / (1000*1000))+ "ms"); System.out.println("threadCount = " + (threadCount) + ";"); System.out.println("loopCount = " + (loopCount) + ";"); System.out.println("sum = " + (sum) + ";"); } // 線程 class AtomicIntegerThread extends Thread { private CountDownLatch latch = null; private CountDownLatch latchdown = null; private int loopCount; public AtomicIntegerThread(CountDownLatch latch, CountDownLatch latchdown, int loopCount) { this.latch = latch; this.latchdown = latchdown; this.loopCount = loopCount; } @Override public void run() { // 等待信號同步 try { this.latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } // for (int i = 0; i < loopCount; i++) { counter_integer.getAndIncrement(); } // 通知遞減1次 latchdown.countDown(); } } // 線程 class IntegerThread extends Thread { private CountDownLatch latch = null; private CountDownLatch latchdown = null; private int loopCount; public IntegerThread(CountDownLatch latch, CountDownLatch latchdown, int loopCount) { this.latch = latch; this.latchdown = latchdown; this.loopCount = loopCount; } @Override public void run() { // 等待信號同步 try { this.latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } // for (int i = 0; i < loopCount; i++) { count_int++; } // 通知遞減1次 latchdown.countDown(); } } }
普通PC機(jī)上的執(zhí)行結(jié)果類似如下:
--------------testAtomic(); 預(yù)期兩者相等------------------- 耗時(shí): 85366ms threadCount = 100; loopCount = 10000600; sum = 1000060000; --------------testIntAdd(); 預(yù)期兩者不相等------------------- 耗時(shí): 1406ms threadCount = 100; loopCount = 10000600; sum = 119428988;
從中可以看出, AtomicInteger操作 與 int操作的效率大致相差在50-80倍上下,當(dāng)然,int很不消耗時(shí)間,這個(gè)對比只是提供一個(gè)參照。
如果確定是單線程執(zhí)行,那應(yīng)該使用 int; 而int在多線程下的操作執(zhí)行的效率還是蠻高的, 10億次只花了1.5秒鐘;
(假設(shè)CPU是 2GHZ,雙核4線程,理論最大8GHZ,則每秒理論上有80億個(gè)時(shí)鐘周期,
10億次Java的int增加消耗了1.5秒,即 120億次運(yùn)算, 算下來每次循環(huán)消耗CPU周期 12個(gè);
個(gè)人覺得效率不錯(cuò), C 語言也應(yīng)該需要4個(gè)以上的時(shí)鐘周期(判斷,執(zhí)行內(nèi)部代碼,自增判斷,跳轉(zhuǎn))
前提是: JVM和CPU沒有進(jìn)行激進(jìn)優(yōu)化.
)
而 AtomicInteger 效率其實(shí)也不低,10億次消耗了80秒, 那100萬次大約也就是千分之一,80毫秒的樣子.
相關(guān)文章
Spring實(shí)戰(zhàn)之XML與JavaConfig的混合配置詳解
大家都知道Spring的顯示配置方式有兩種,一種是基于XML配置,一種是基于JavaConfig的方式配置。那么下這篇文章主要給大家分別介紹如何在JavaConfig中引用XML配置的bean以及如何在XML配置中引用JavaConfig,需要的朋友可以參考下。2017-07-07SpringBoot2.6.x升級后循環(huán)依賴及Swagger無法使用問題
這篇文章主要為大家介紹了SpringBoot2.6.x升級后循環(huán)依賴及Swagger無法使用問題,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06SpringMVC中RequestMapping注解(作用、出現(xiàn)的位置、屬性)
這篇文章主要介紹了SpringMVC中RequestMapping注解(作用、出現(xiàn)的位置、屬性),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01NoHttpResponseException問題分析解決記錄
這篇文章主要為大家介紹了NoHttpResponseException問題分析解決記錄,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08Springboot Vue可配置調(diào)度任務(wù)實(shí)現(xiàn)示例詳解
這篇文章主要為大家介紹了Springboot Vue可配置調(diào)度任務(wù)實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01