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