<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>

          AtomicXXX 用的好好的,阿里為什么推薦使用 LongAdder?面試必問(wèn)!

          共 2066字,需瀏覽 5分鐘

           ·

          2022-03-10 18:31

          點(diǎn)擊關(guān)注公眾號(hào),Java干貨及時(shí)送達(dá)

          面試連環(huán)炮

          先來(lái)一連炮簡(jiǎn)單的面試,看你能頂住幾輪?


          棧長(zhǎng):

          1、多線(xiàn)程情況下,進(jìn)行數(shù)字累加(count++)要注意什么?

          張三:

          要注意給累加方法加同步鎖,不然會(huì)出現(xiàn)變量可見(jiàn)性問(wèn)題,變量值被其他線(xiàn)程覆蓋出現(xiàn)不一致的情況

          棧長(zhǎng):

          2、保證變量可見(jiàn)性,用 volatile 修飾不就行了嗎?

          張三:

          volatile 是可以保證可見(jiàn)性,但不能保證原子性和線(xiàn)程安全

          棧長(zhǎng):

          3、除了加同步鎖這種方案,還有別的方法嗎?

          張三:

          還可以用 JDK 中的原子類(lèi),比如:AtomicInteger、AtomicLong,它們是通過(guò) CAS 算法實(shí)現(xiàn)的一種樂(lè)觀鎖

          棧長(zhǎng):

          4、不錯(cuò),還知道別的么?

          張三:

          呃……


          認(rèn)真的,你能頂住幾輪?

          這些問(wèn)題是 Java 程序員面試過(guò)程中必問(wèn)的,出場(chǎng)率賊高,Java 程序員必懂,這些題在Java面試庫(kù)小程序中也都有詳細(xì)答案,這里就不展開(kāi)了。

          你還知道別的么?最后一輪的答案就是今天的主題!

          更好的選擇:LongAdder

          你還在用 AtomicInteger、AtomicLong 原子類(lèi)進(jìn)行并發(fā)累加操作嗎?那你就 OUT 了!

          除了 AtomicInteger、AtomicLong,其實(shí)在 JDK 8 中更建議使用 LongAdder 進(jìn)行原子性操作,性能更好,如果你使用的還是 JDK 7-,那當(dāng)我沒(méi)說(shuō),即使如此,也不能找借口不知道,畢竟 JDK 8 是現(xiàn)在的主流應(yīng)用版本了。

          阿里巴巴最新的 Java開(kāi)發(fā)手冊(cè) 是這么定義的:

          這份阿里巴巴完整的 Java 開(kāi)發(fā)手冊(cè),可以關(guān)注公眾號(hào):Java核心技術(shù),回復(fù):手冊(cè),即可下載高清完整版。

          如果你還沒(méi)有用過(guò) LongAdder,不妨看看本文,刷新你的認(rèn)知,棧長(zhǎng)帶你漲知識(shí)!

          為什么搞出了 LongAdder?

          我們都知道在 JDK 5 中搞出了 AtomicInteger、AtomicLong 等原子類(lèi),這也是在 JDK 8 之前普遍用的原子性操作類(lèi),來(lái)看下 AtomicLong 的累加源碼:

          大家都知道這些原子類(lèi)都是通過(guò) CAS 算法實(shí)現(xiàn)的樂(lè)觀鎖,通過(guò)舊值和現(xiàn)有的值不斷循環(huán)比對(duì),直到比對(duì)成功才修改成功結(jié)束循環(huán)。

          這樣就會(huì)有一個(gè)問(wèn)題,如果并發(fā)數(shù)很高的話(huà),就會(huì)造成過(guò)多的沒(méi)有必要的 "循環(huán)",這勢(shì)必會(huì)影響 CPU 的性能。

          所以,JDK 8 又搞出來(lái)了一個(gè) LongAdder,也在 atomic 包下:

          大家可以看到,在同級(jí)包中還有一個(gè) LongAccumulator 類(lèi),這個(gè)這篇不展開(kāi),棧長(zhǎng)下次再另開(kāi)一篇具體分析,關(guān)注公眾號(hào):Java技術(shù)棧,寫(xiě)完我會(huì)第一時(shí)間進(jìn)行推送。

          LongAdder 為什么性能更好?

          來(lái)分析下 LongAdder 類(lèi)的源碼:

          累加

          在 LongAdder 中維護(hù)了一個(gè) Cell 數(shù)組,當(dāng) Cell 它不為空時(shí),size 是 2 的次冪大小,每個(gè) Cell 數(shù)組里面都有一個(gè)初始值為 0 的 long 變量,用來(lái)存儲(chǔ)每個(gè) Cell 的值:

          Cell 類(lèi)源碼

          然后其中的 sum 方法用來(lái)對(duì) Cell 數(shù)組進(jìn)行求和再加上 base 基礎(chǔ)值進(jìn)行返回:

          求和

          關(guān)于 base 基礎(chǔ)值:

          LongAdder 并不會(huì)一開(kāi)始就創(chuàng)建 Cell 數(shù)組,其本身也會(huì)維護(hù)一個(gè) base 基礎(chǔ)值,當(dāng) CAS 更新失敗時(shí)才進(jìn)行創(chuàng)建或者擴(kuò)容。

          來(lái)看下 AtomicXXX 和 LongAdder 更新對(duì)比圖:

          來(lái)源:https://acet.pe.kr/809

          Cell 數(shù)組相當(dāng)于一個(gè)分段的概念,把 AtomicXXX 中的一個(gè)值分成了多個(gè)值進(jìn)行管理,當(dāng) CAS 更新失敗時(shí)不再當(dāng)前循環(huán)重試,而是嘗試獲取其他的資源鎖,這樣就降低了對(duì)于 AtomicXXX 中的單個(gè)資源的競(jìng)爭(zhēng),所以 LongAdder 的性能更高。

          雖然 LongAdder 性能更好,那有沒(méi)有缺點(diǎn)呢?

          LongAdder 帶來(lái)了良好的性能,代價(jià)肯定也是有的,既然維護(hù)了 Cell 數(shù)組,也就意味著要占用更多的內(nèi)存空間,以空間換時(shí)間,也是值得的。

          實(shí)戰(zhàn)測(cè)試

          既然官方都說(shuō)在高并發(fā)的情況下性能更好,事實(shí)是否如此呢?

          棧長(zhǎng)必須實(shí)戰(zhàn)測(cè)試一翻,打消大家的疑慮!

          AtomicLong 測(cè)試代碼:

          /**
          ?*?@author:?棧長(zhǎng)
          ?*?@from:?公眾號(hào)Java技術(shù)棧
          ?*/
          private?static?void?atomicLongTest()?throws?InterruptedException?{
          ????long?start?=?System.currentTimeMillis();
          ????ExecutorService?es?=?Executors.newFixedThreadPool(MAX_POOL_SIZE);
          ????for?(int?i?=?0;?i?????????es.execute(()?->?{
          ????????????for?(int?j?=?0;?j?????????????????atomicLong.incrementAndGet();
          ????????????}
          ????????});
          ????}
          ????es.shutdown();
          ????es.awaitTermination(5,?TimeUnit.MINUTES);
          ????System.out.printf("AtomicLong %s*%s 結(jié)果:%s,耗時(shí):%sms.\n",
          ????????????MAX_POOL_SIZE,
          ????????????MAX_LOOP_SIZE,
          ????????????atomicLong.get(),
          ????????????(System.currentTimeMillis()?-?start));
          }

          LongAdder 測(cè)試代碼:

          /**
          ?*?@author:?棧長(zhǎng)
          ?*?@from:?公眾號(hào)Java技術(shù)棧
          ?*/
          private?static?void?longAdderTest()?throws?InterruptedException?{
          ????long?start?=?System.currentTimeMillis();
          ????ExecutorService?es?=?Executors.newFixedThreadPool(MAX_POOL_SIZE);
          ????for?(int?i?=?0;?i?????????es.execute(()?->?{
          ????????????for?(int?j?=?0;?j?????????????????longAdder.increment();
          ????????????}
          ????????});
          ????}
          ????es.shutdown();
          ????es.awaitTermination(5,?TimeUnit.MINUTES);
          ????System.out.printf("LongAdder %s*%s 結(jié)果:%s,耗時(shí):%sms.\n",
          ????????????MAX_POOL_SIZE,
          ????????????MAX_LOOP_SIZE,
          ????????????longAdder.sum(),
          ????????????(System.currentTimeMillis()?-?start));
          }

          這里只貼核心測(cè)試代碼了,完整代碼已上傳到了 Github:

          https://github.com/javastacks/javastack

          測(cè)試結(jié)果:

          這里測(cè)試的只有是 1 個(gè)線(xiàn)程,每個(gè)線(xiàn)程循環(huán)累加 1 次,這個(gè)沒(méi)有鎖競(jìng)爭(zhēng)、沒(méi)有高并發(fā)操作的場(chǎng)景就能看出性能上的差異了。。

          棧長(zhǎng)再不斷提升 線(xiàn)程數(shù)、循環(huán)累加次數(shù) ,得到了以下測(cè)試結(jié)果:

          線(xiàn)程數(shù) * 循環(huán)次數(shù)AtomicLongLongAdder
          1 * 145ms1ms
          10 * 1055ms2ms
          10 * 10056ms2ms
          100 * 1058ms10ms
          100 * 10074ms10ms
          1000 * 10190ms71ms
          1000 * 100217ms73ms
          1000 * 1000194ms81ms
          1000 * 10000301ms114ms
          1000 * 1000001813ms277ms
          1000 * 100000017596ms1629ms

          圖表對(duì)比:

          從測(cè)試結(jié)果可以看出,LongAdder 的性能都是碾壓 AtomicLong 的,最高可達(dá) 28 多倍的差距(56/2),可以說(shuō)在高性能要求的高并發(fā)場(chǎng)景,肯定是有必要用 LongAdder 的,這也是阿里巴巴為什么建議使用 LongAdder 的原因。

          當(dāng)然,這只是我個(gè)人的測(cè)試,這個(gè)也和硬件配置有關(guān)系的,但毋庸置疑是,AtomicLong 的性能是更好的。另外,棧長(zhǎng)寫(xiě)的 Java 多線(xiàn)程系列教程全部整理好了,微信搜索Java技術(shù)棧,在后臺(tái)發(fā)送:Java,可以在線(xiàn)閱讀。

          總結(jié)

          本文以一場(chǎng)面試連環(huán)炮揭開(kāi)了 LongAdder 的面紗,怎么解決 count++ 的線(xiàn)程安全性問(wèn)題?

          棧長(zhǎng)再總結(jié)下:

          • 累加方法加 synchronized/ Lock 同步鎖;
          • 使用 AtomicInteger/ AtomicLong 原子類(lèi);
          • 使用 LongAdder 原子類(lèi)(推薦使用);

          LongAdder 這個(gè)東西是 Java 8 搞出來(lái)的,用來(lái)代替 AtomicXXX,不管是否高并發(fā)場(chǎng)景,都完勝 AtomicXXX,它不僅可以改善性能,現(xiàn)在面試也問(wèn)的越來(lái)越多了,大家還是有必要掌握。

          雖然 LongAdder 性能更好,但也是以更多的內(nèi)存空間消耗為代價(jià)的,當(dāng)然,現(xiàn)代計(jì)算機(jī),內(nèi)存早已不是瓶頸,所以這點(diǎn)消耗是可以忽略不計(jì)的,性能還是最重要的,但是大家也要知道這個(gè)點(diǎn)。

          本文實(shí)戰(zhàn)源代碼完整版已經(jīng)上傳:

          https://github.com/javastacks/javastack

          歡迎 Star 學(xué)習(xí),公眾號(hào)所有 Java 實(shí)戰(zhàn)示例都會(huì)在這上面提供!

          好了,今天的分享就到這里了,后面棧長(zhǎng)會(huì)分享更多好玩的 Java 技術(shù)和最新的技術(shù)資訊,關(guān)注公眾號(hào)Java技術(shù)棧第一時(shí)間推送,我也將主流 Java 面試題和參考答案都整理好了,在公眾號(hào)后臺(tái)回復(fù)關(guān)鍵字 "面試" 進(jìn)行刷題。

          最后,覺(jué)得我的文章對(duì)你用收獲的話(huà),動(dòng)動(dòng)小手,給個(gè)在看、轉(zhuǎn)發(fā),原創(chuàng)不易,棧長(zhǎng)需要你的鼓勵(lì)。

          版權(quán)聲明: 本文系公眾號(hào) "Java技術(shù)棧" 原創(chuàng),轉(zhuǎn)載、引用本文內(nèi)容請(qǐng)注明出處,抄襲、洗稿一律投訴侵權(quán),后果自負(fù),并保留追究其法律責(zé)任的權(quán)利。








          Spring Cloud 爆高危漏洞,趕緊修復(fù)!
          2021 年發(fā)生的 10 件技術(shù)大事!!
          23 種設(shè)計(jì)模式實(shí)戰(zhàn)(很全)
          換掉 Log4j2!tinylog 橫空出世
          再見(jiàn)單身狗!Java 創(chuàng)建對(duì)象的 6 種方式
          勁爆!Java 協(xié)程要來(lái)了
          重磅官宣:Redis 對(duì)象映射框架來(lái)了!!
          別再寫(xiě)爆爆爆炸類(lèi)了,試試裝飾器模式!
          程序員精通各種技術(shù)體系,45歲求職難!
          Spring Boot 3.0 M1 發(fā)布,正式棄用 Java 8
          Spring Boot 學(xué)習(xí)筆記,這個(gè)太全了!



          關(guān)注Java技術(shù)棧看更多干貨



          獲取 Spring Boot 實(shí)戰(zhàn)筆記!
          瀏覽 88
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  国产精品九| 在线视频亚洲 | 大香蕉导航 | 欧美成人精品免费 | 久久青青操 |