<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          阿里為什么推薦使用LongAdder,而不是volatile?

          共 1034字,需瀏覽 3分鐘

           ·

          2020-08-27 00:41

          這是我的第?87?篇原創(chuàng)文章

          作者 | 王磊

          來源 | Java中文社群(ID:javacn666)

          轉(zhuǎn)載請聯(lián)系授權(quán)(微信ID:GG_Stone)

          阿里《Java開發(fā)手冊》最新嵩山版在 8.3 日發(fā)布,其中有一段內(nèi)容引起了老王的注意,內(nèi)容如下:

          【參考】volatile 解決多線程內(nèi)存不可見問題。對于一寫多讀,是可以解決變量同步問題,但是如果多寫,同樣無法解決線程安全問題。

          說明:如果是 count++ 操作,使用如下類實現(xiàn):AtomicInteger count = new AtomicInteger(); count.addAndGet(1); 如果是 JDK8,推薦使用 LongAdder 對象,比 AtomicLong 性能更好(減少樂觀 鎖的重試次數(shù))。

          以上內(nèi)容共有兩個重點:

          1. 類似于 count++ 這種非一寫多讀的場景不能使用 volatile
          2. 如果是 JDK8 推薦使用 LongAdder 而非 AtomicLong 來替代 volatile,因為 LongAdder 的性能更好。

          但口說無憑,即使是孤盡大佬說的,咱們也得證實一下,因為馬老爺子說過:實踐是檢驗真理的唯一標準

          這樣做也有它的好處,第一,加深了我們對知識的認知;第二,文檔上只寫了LongAdderAtomicLong 的性能高,但是高多少呢?文中并沒有說,那只能我們自己動手去測試嘍。

          話不多,接下來我們直接進入本文正式內(nèi)容...

          volatile 線程安全測試

          首先我們來測試 volatile 在多寫環(huán)境下的線程安全情況,測試代碼如下:

          public?class?VolatileExample?{
          ????public?static?volatile?int?count?=?0;?//?計數(shù)器
          ????public?static?final?int?size?=?100000;?//?循環(huán)測試次數(shù)

          ????public?static?void?main(String[]?args)?{
          ????????//?++?方式?10w?次
          ????????Thread?thread?=?new?Thread(()?->?{
          ????????????for?(int?i?=?1;?i?<=?size;?i++)?{
          ????????????????count++;
          ????????????}
          ????????});
          ????????thread.start();
          ????????//?--?10w?次
          ????????for?(int?i?=?1;?i?<=?size;?i++)?{
          ????????????count--;
          ????????}
          ????????//?等所有線程執(zhí)行完成
          ????????while?(thread.isAlive())?{}
          ????????System.out.println(count);?//?打印結(jié)果
          ????}
          }

          我們把 volatile 修飾的 count 變量 ++ 10w 次,在啟動另一個線程 -- 10w 次,正常來說結(jié)果應(yīng)該是 0,但是我們執(zhí)行的結(jié)果卻為:

          1063

          結(jié)論:由以上結(jié)果可以看出 volatile 在多寫環(huán)境下是非線程安全的,測試結(jié)果和《Java開發(fā)手冊》相吻合。

          LongAdder VS AtomicLong

          接下來,我們使用 Oracle 官方的 JMH(Java Microbenchmark Harness, JAVA 微基準測試套件)來測試一下兩者的性能,測試代碼如下:

          import?org.openjdk.jmh.annotations.*;
          import?org.openjdk.jmh.infra.Blackhole;
          import?org.openjdk.jmh.runner.Runner;
          import?org.openjdk.jmh.runner.RunnerException;
          import?org.openjdk.jmh.runner.options.Options;
          import?org.openjdk.jmh.runner.options.OptionsBuilder;

          import?java.util.concurrent.TimeUnit;
          import?java.util.concurrent.atomic.AtomicInteger;
          import?java.util.concurrent.atomic.LongAdder;

          @BenchmarkMode(Mode.AverageTime)?//?測試完成時間
          @OutputTimeUnit(TimeUnit.NANOSECONDS)
          @Warmup(iterations?=?1,?time?=?1,?timeUnit?=?TimeUnit.SECONDS)?//?預(yù)熱?1?輪,每次?1s
          @Measurement(iterations?=?5,?time?=?5,?timeUnit?=?TimeUnit.SECONDS)?//?測試?5?輪,每次?3s
          @Fork(1)?//?fork?1?個線程
          @State(Scope.Benchmark)
          @Threads(1000)?//?開啟?1000?個并發(fā)線程
          public?class?AlibabaAtomicTest?{

          ????public?static?void?main(String[]?args)?throws?RunnerException?{
          ????????//?啟動基準測試
          ????????Options?opt?=?new?OptionsBuilder()
          ????????????????.include(AlibabaAtomicTest.class.getSimpleName())?//?要導(dǎo)入的測試類
          ????????????????.build();
          ????????new?Runner(opt).run();?//?執(zhí)行測試
          ????}

          ????@Benchmark
          ????public?int?atomicTest(Blackhole?blackhole)?throws?InterruptedException?{
          ????????AtomicInteger?atomicInteger?=?new?AtomicInteger();
          ????????for?(int?i?=?0;?i?1024;?i++)?{
          ????????????atomicInteger.addAndGet(1);
          ????????}
          ????????//?為了避免?JIT?忽略未被使用的結(jié)果
          ????????return?atomicInteger.intValue();
          ????}

          ????@Benchmark
          ????public?int?longAdderTest(Blackhole?blackhole)?throws?InterruptedException?{
          ????????LongAdder?longAdder?=?new?LongAdder();
          ????????for?(int?i?=?0;?i?1024;?i++)?{
          ????????????longAdder.add(1);
          ????????}
          ????????return?longAdder.intValue();
          ????}
          }

          程序執(zhí)行的結(jié)果為:

          從上述的數(shù)據(jù)可以看出,在開啟了 1000 個線程之后,程序的 LongAdder 的性能比 AtomicInteger 快了約 1.53 倍,你沒看出是開了 1000 個線程,為什么要開這么多呢?這其實是為了模擬高并發(fā)高競爭的環(huán)境下二者的性能查詢。

          如果在低競爭下,比如我們開啟 100 個線程,測試的結(jié)果如下:

          結(jié)論:從上面結(jié)果可以看出,在低競爭的并發(fā)環(huán)境下 AtomicInteger 的性能是要比 LongAdder 的性能好,而高競爭環(huán)境下 LongAdder 的性能比 AtomicInteger,當(dāng)有 1000 個線程運行時,LongAdder 的性能比 AtomicInteger 快了約 1.53 倍,所以各位要根據(jù)自己業(yè)務(wù)情況選擇合適的類型來使用。

          性能分析

          為什么會出現(xiàn)上面的情況?這是因為 AtomicInteger 在高并發(fā)環(huán)境下會有多個線程去競爭一個原子變量,而始終只有一個線程能競爭成功,而其他線程會一直通過 CAS 自旋嘗試獲取此原子變量,因此會有一定的性能消耗;而 LongAdder 會將這個原子變量分離成一個 Cell 數(shù)組,每個線程通過 Hash 獲取到自己數(shù)組,這樣就減少了樂觀鎖的重試次數(shù),從而在高競爭下獲得優(yōu)勢;而在低競爭下表現(xiàn)的又不是很好,可能是因為自己本身機制的執(zhí)行時間大于了鎖競爭的自旋時間,因此在低競爭下表現(xiàn)性能不如 AtomicInteger

          總結(jié)

          本文我們測試了 volatile 在多寫情況下是非線程安全的,而在低競爭的并發(fā)環(huán)境下 AtomicInteger 的性能是要比 LongAdder 的性能好,而高競爭環(huán)境下 LongAdder 的性能比 AtomicInteger,因此我們在使用時要結(jié)合自身的業(yè)務(wù)情況來選擇相應(yīng)的類型。

          1.?人人都能看懂的 6 種限流實現(xiàn)方案!

          2.?一個空格引發(fā)的“慘案“

          3.?大型網(wǎng)站架構(gòu)演化發(fā)展歷程

          4.?Java語言“坑爹”排行榜TOP 10

          5. 我是一個Java類(附帶精彩吐槽)

          6. 看完這篇Redis緩存三大問題,保你能和面試官互扯

          7. 程序員必知的 89 個操作系統(tǒng)核心概念

          8. 深入理解 MySQL:快速學(xué)會分析SQL執(zhí)行效率

          9. API 接口設(shè)計規(guī)范

          10. Spring Boot 面試,一個問題就干趴下了!



          掃碼二維碼關(guān)注我


          ·end·

          —如果本文有幫助,請分享到朋友圈吧—

          我們一起愉快的玩耍!



          你點的每個贊,我都認真當(dāng)成了喜歡
          瀏覽 56
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  精品蜜桃视频 | 日韩免费成人电影 | 日本天堂一区在线 | a在线免费观看视频 | 国产剧情一区二区av在线观看 |