原子操作之 LongAccumulator
1. 前言
今天為大家介紹原子操作之 LongAccumulator。此工具位于 java.util.concurrent.atomic 包中。
本節(jié)先介紹 LongAccumulator 工具類的基本概念和最基本用法,之后給出 LongAccumulator 工具類最常用的場合說明,然后通過簡單的編碼實現(xiàn)一個實際案例,最后帶領(lǐng)大家熟悉 LongAccumulator 最常用的一些編程方法,讓大家進一步加深對 LongAccumulator 工具類的理解。
下面我們正式開始介紹吧。
2. 概念介紹
相比 LongAdder,LongAccumulator 工具類提供了更靈活更強大的功能。不但可以指定計算結(jié)果的初始值,相比 LongAdder 只能對數(shù)值進行加減運算,LongAccumulator 還能自定義計算規(guī)則,比如做乘法運行,或其他任何你想要的計算規(guī)則。這樣描述是不是有點抽象,別著急,看下面的圖示。
我們看下面 LongAccumulator 工具類的基本用法。
3. 基本用法
// 首先創(chuàng)建一個雙目運算器對象,這個對象實現(xiàn)了計算規(guī)則。
LongBinaryOperator longBinaryOperator = new LongBinaryOperator() {
@Override
public long applyAsLong(long left, long right) {
...
}
}
// 接著使用構(gòu)造方法創(chuàng)建一個 LongAccumulator 對象,這個對象的第1個參數(shù)就是一個雙目運算器對象,第二個參數(shù)是累加器的初始值。
LongAccumulator longAccumulator = new LongAccumulator(longBinaryOperator, 0);
...
// 調(diào)用累加方法
longAccumulator.accumulate(1000)
// 調(diào)用結(jié)果獲取方法
long result = longAccumulator.get();
...
是不是簡單又強大!LongAccumulator 在我們?nèi)粘嵺`中,到底應(yīng)該應(yīng)用在哪些場合比較合適呢?下面我們給出最常用的場景說明。
4. 常用場景
LongAccumulator 經(jīng)常用于自定義運算規(guī)則場景下的多線程并發(fā)場合。一些簡單的累加計算可以直接使用我們之前課程中介紹的工具類,但是當(dāng)運行規(guī)則比較復(fù)雜或者 JDK 沒有提供對應(yīng)的工具類時,可以考慮 LongAccumulator 輔助實現(xiàn)。當(dāng)然所有可使用 LongAdder 的場合都可使用 LongAccumulator 代替,但是沒有必要。
下面我們用 LongAccumulator 工具類實現(xiàn)上一節(jié)中的生活實例,為了簡化敘述,本節(jié)我們只統(tǒng)計男性客戶總數(shù)量。請看下面的代碼。
5. 場景案例
import java.util.concurrent.atomic.LongAccumulator;
public class LongAccumulatorTest {
// 此處的運算規(guī)則是累加,所以創(chuàng)建一個加法雙目運算器對象作為構(gòu)造函數(shù)的第一個參數(shù)。
// 將第二個參數(shù)置為0,表示累加初始值。
// maleCount 對象代表當(dāng)天所有進入商場的男性客戶總數(shù)量。
private static LongAccumulator maleCount = new LongAccumulator(new LongBinaryOperator() {
// 此方法用于實現(xiàn)計算規(guī)則
@Override
public long applyAsLong(long left, long right) {
// 在本例中使用加法計算規(guī)則
return left + right;
}
}, 0);
public static void main(String[] args) {
// 定義30個商場入口檢測設(shè)備
for (int i = 1; i <= 30; i++) {
MonitoringDevice monitoringDevice = new MonitoringDevice(maleCount, i);
// 開啟檢測設(shè)備進行檢測
new Thread(monitoringDevice).start();
}
}
}
在上面的代碼中,首先創(chuàng)建一個 LongAccumulator 對象表示統(tǒng)計結(jié)果,然后創(chuàng)建了 30 個商場入口檢測設(shè)備模擬檢測識別,接下來每個檢測設(shè)備如何動作呢,看下面的代碼。
import java.util.Random;
import java.util.concurrent.atomic.LongAccumulator;
/**
* 模擬設(shè)備
*/
public class MonitoringDevice implements Runnable {
private LongAccumulator maleCount;
private String monitoringDeviceNo;
public MonitoringDevice(LongAccumulator maleCount, int monitoringDeviceNo) {
this.maleCount = maleCount;
this.monitoringDeviceNo = "第" + monitoringDeviceNo + "監(jiān)控采集處";
}
/**
* 設(shè)備運行的處理邏輯
*/
public void run() {
while (true) {
// 監(jiān)測處理 (監(jiān)測設(shè)備輸出1代表男性,0代表女性,其他代表未能識別,此處隨機產(chǎn)生監(jiān)測結(jié)果)
try {
Thread.sleep(new Random().nextInt(3000));
} catch (Exception e) {}
int monitoringDeviceOutput = new Random().nextInt(3);
// 對監(jiān)測結(jié)果進行統(tǒng)計
switch (monitoringDeviceOutput) {
case 1: maleCount.accumulate(1);
System.out.println("統(tǒng)計結(jié)果: maleCount=" + maleCount.get());
break;
default:
System.out.println("忽略統(tǒng)計");
break;
}
}
}
}
在 MonitoringDevice 類中,首先模擬監(jiān)測設(shè)備輸出,然后將輸出結(jié)果使用 add () 進行統(tǒng)計累加,使用 sum () 輸出累加結(jié)果。運行一段時間后運行結(jié)果如下。
...
忽略統(tǒng)計
統(tǒng)計結(jié)果: maleCount=50
...
上面的示例中,使用 LongAccumulator 實現(xiàn)了上一節(jié)中相同的需求。對比觀察,能夠體會到 LongAccumulator 工具類更靈活的地方,但同時也更復(fù)雜一些。
至此,大家對 LongAccumulator 已經(jīng)有了初步的理解,接下來我們繼續(xù)豐富對 LongAccumulator 工具類的認識。
6. 核心方法介紹
除過上面代碼中使用的最基本的 accumulate (int)、get () 方法之外,我們再介紹兩個方法的使用。
- reset () 方法
將累加器值置為 0,即為后繼使用重新歸位。
- getThenReset () 方法
此方法邏輯等同于先調(diào)用 get () 方法再調(diào)用 reset () 方法。
7. 小結(jié)
本節(jié)通過一個簡單的例子,介紹了 LongAccumulator 的基本用法。在 java.util.concurrent.atomic 包中還存在類似的工具類,如 DoubleAccumulator,用法大同小異,希望大家在日常研發(fā)中多比較多總結(jié),早日掌握之。