爲什麼阿里不推薦使用 AtomicLong?

作者:伴川
來源:blog.csdn.net/kologin/article/details/135126371

前言

在分佈式系統中,計數器是一個常見的需求。爲了實現高併發、高可用的計數器,我們需要選擇一個合適的實現方式。

在 Java 中,有兩種常見的計數器實現方式:AtomicLong 和 LongAdder。

最近,阿里巴巴在一份技術報告中推薦使用 LongAdder ,而不是 AtomicLong

本文將介紹這兩種計數器的原理和優缺點,並分析爲什麼阿里巴巴推薦使用 LongAdder 。

推薦一個開源免費的 Spring Boot 實戰項目:

https://github.com/javastacks/spring-boot-best-practice

一、CAS

1.1 CAS 全稱

全稱:compare and swap,比較並交換。

雖然翻譯過來是[比較並交換],但它是一個原子性的操作,對應到CPU指令爲 cmpxchg 。

1.2 通俗理解CAS

  • CAS 有三個操作數:當前值A、內存值V、要修改的新值B。
  • 假設 當前值A 跟 內存值V 相等,那就將內存值V 改成B。
  • 假設 當前值A 跟 內存值V 不相等,要麼就重試,要麼就放棄更新。
  • 將當前值與內存值進行對比,判斷是否有被修改過,這就是CAS的核心。

1.3 CAS的問題

CAS有個缺點就是會帶來 ABA 的問題。

從CAS更新的時候,我們可以發現它只比對當前值和內存值是否相等,這會帶來個問題,下面我舉例說明下:

  • 假設線程A讀到當前值是10,可能線程B把值修改爲100,然後線程C又把值修改爲10。
  • 等到線程A拿到執行權時,因爲當前值和內存值是一致的,線程A是可以修改的!
  • 站在線程A的角度來說,這個值是從未被修改的 。
  • 這是不合理的,因爲我們從上帝的角度來看,這個變量已經被線程B和線程C修改過了。

1.4 解決 ABA 問題

要解決ABA的問題,Java 也提供了 AtomicStampedReference 類供我們用,說白了就是加了個版本,比對的就是內存值+版本是否一致。

疑問:

爲什麼阿里巴巴開發手冊提及到推薦使用 LongAdder 對象,比AtomicLong 性能更好(減少樂觀鎖的重試次數)?

原因:

因爲 AtomicLong 做累加的時候實際上就是多個線程操作同一個目標資源。

在高併發時,只有一個線程是執行成功的,其他的線程都會失敗,不斷自旋(重試),自旋會成爲瓶頸。

而 LongAdder 的思想就是把要操作的目標資源 分散,到數組 Cell 中。

每個線程對自己的 Cell 變量的 value 進行原子操作,大大降低了失敗的次數。

這就是爲什麼在高併發場景下,推薦使用 LongAdder 的原因。

二、LongAdder

2.1 什麼是 LongAdder

LongAdder是JDK1.8由Doug Lea大神新增的原子操作類,位於java.util.concurrent.atomic包下,LongAdder在高併發的場景下會比AtomicLong 具有更好的性能,代價是消耗更多的內存空間。

LongAdder是Google開源的一個高性能計數器實現。它採用了一種分段鎖的策略,將一個long型的變量分割成多個16字節的段,每個段都使用一個獨立的AtomicLong進行更新。這樣,在高併發場景下,多個線程可以同時對不同的段進行更新操作,互不干擾。

LongAdder的優點是併發性能高,適用於高併發的場景。由於採用了分段鎖的策略,LongAdder可以避免AtomicLong中的競爭問題。此外,LongAdder還支持可擴展性,可以通過增加更多的段來提高性能。但是,LongAdder的缺點是代碼相對複雜一些,需要更多的維護成本。

2.2 爲什麼推薦推薦 LongAdder

LongAdder設計思想上,採用分段的方式降低併發衝突的概率。通過維護一個基準值base和 Cell 數組:

如下圖所示:

三、AtomicLong

3.1 什麼是 AtomicLong

AtomicLong是Java提供的一個原子類,用於實現高併發的計數器。它利用了CAS(Compare-and-Swap)操作來保證線程安全。在AtomicLong中,每次計數操作都會先讀取當前值,然後使用CAS操作更新值。如果值沒有被其他線程修改過,則更新成功,否則需要重新嘗試。

AtomicLong的優點是簡單易用,性能也不錯。但是,在高併發場景下,AtomicLong可能會出現競爭問題。因爲多個線程可能同時讀取和更新同一個AtomicLong的當前值,導致數據不一致。此外,AtomicLong的CAS操作也可能因爲硬件和操作系統的原因出現失敗的情況。

3.2 爲什麼不推薦 AtomicLong

在LongAdder之前,當我們在進行計數統計的時,通常會使用AtomicLong來實現。AtomicLong能保證併發情況下計數的準確性,其內部通過CAS來解決併發安全性的問題。

如下圖所示:

圖裏可以看出在高併發情況下,當有大量線程同時去更新一個變量,任意一個時間點只有一個線程能夠成功,絕大部分的線程在嘗試更新失敗後,會通過自旋的方式再次進行嘗試,這樣嚴重佔用了CPU的時間片,進而導致系統性能問題。

四、總結

阿里巴巴推薦使用LongAdder的原因主要有以下幾點:

1.高併發性能:

LongAdder採用分段鎖的策略,可以避免AtomicLong中的競爭問題,提高併發性能。在分佈式系統中,高併發性能是非常重要的。

2.可擴展性:

LongAdder支持可擴展性,可以通過增加更多的段來提高性能。這對於需要處理大量請求的分佈式系統來說是非常有利的。

3.代碼簡單易懂:

雖然LongAdder的代碼相對複雜一些,但是相對於AtomicLong來說更容易理解和維護。這對於開發人員來說是非常重要的。

4.更好的適用場景:

阿里巴巴推薦使用LongAdder主要是因爲在分佈式系統中需要一個高性能、高可用的計數器實現。而LongAdder正好符合這個需求。

總之,阿里巴巴推薦使用LongAdder的原因主要是因爲它的高併發性能、可擴展性、代碼簡單易懂以及更好的適用場景。當然,在實際應用中還需要根據具體場景和需求進行選擇和優化。

更多文章推薦:

1.Spring Boot 3.x 教程,太全了!

2.2,000+ 道 Java面試題及答案整理(2024最新版)

3.免費獲取 IDEA 激活碼的 7 種方式(2024最新版)

覺得不錯,別忘了隨手點贊+轉發哦!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章