原子操作之 DoubleAdder
1. 前言
今天為大家介紹原子操作之 DoubleAdder。此工具位于 java.util.concurrent.atomic 包中。
本節(jié)先介紹 DoubleAdder 工具類的基本概念和最基本用法,之后給出 DoubleAdder 工具類最常用的場合說明,然后通過簡單的編碼實現(xiàn)一個實際案例,讓大家有一個理性的認識,最后帶領(lǐng)大家熟悉 DoubleAdder 最常用的一些編程方法,進一步加深對 DoubleAdder 工具類的理解。
下面我們正式開始介紹吧。
2. 概念介紹
DoubleAdder 工具類采用了 “分頭計算最后匯總” 的思路,避免每一次(細粒度)操作的并發(fā)控制,提高了并發(fā)的性能。什么是細粒度的同步控制呢?所謂細粒度的同步控制,指的是對待同步控制對象的每一次操作都需要加以控制,這樣描述是不是有點抽象,別著急,看下面的圖示。
我們看下面 DoubleAdder 工具類的基本用法。
3. 基本用法
// 首先創(chuàng)建一個 DoubleAdder 對象
DoubleAdder doubleAdder = new DoubleAdder();
...
// 調(diào)用累加方法
doubleAdder.add(50.5);
doubleAdder.add(49.5);
// 調(diào)用求和方法
double sum = doubleAdder.sum();
...
是不是很簡單,那 DoubleAdder 在我們?nèi)粘嵺`中,到底應(yīng)該應(yīng)用在哪些場合比較合適呢?下面我們給出最常用的場景說明。
4. 常用場景
DoubleAdder 經(jīng)常用于多線程并發(fā)做收集統(tǒng)計數(shù)據(jù)的場合,而不是細粒度的同步控制。
下面我們用 DoubleAdder 工具類實現(xiàn)一個生活案例:某商場為了掌握客流特征,在商場所有出入口架設(shè)了人體特征識別設(shè)備,此類設(shè)備可以有效識別客人性別等信息?;诖耍虉龉芾磙k公室計劃制作一個客流性別流量圖表,用于決策商場的服務(wù)內(nèi)容。
5. 場景案例
import java.util.concurrent.atomic.DoubleAdder;
public class DoubleAdderTest {
// 首先創(chuàng)建三個 DoubleAdder 對象分別表示統(tǒng)計結(jié)果
// 代表當(dāng)天所有進入商場的男性客戶總數(shù)量
private static DoubleAdder maleCount = new DoubleAdder();
// 代表當(dāng)天所有進入商場的女性客戶總數(shù)量
private static DoubleAdder womenCount = new DoubleAdder();
// 代表當(dāng)天所有進入商場的未能識別的客戶總數(shù)量
private static DoubleAdder unknownGenderCount = new DoubleAdder();
public static void main(String[] args) {
// 定義30個商場入口檢測設(shè)備
for (int i = 1; i <= 30; i++) {
MonitoringDevice monitoringDevice = new MonitoringDevice(maleCount, womenCount, unknownGenderCount, i);
// 開啟檢測設(shè)備進行檢測
new Thread(monitoringDevice).start();
}
}
}
在上面的代碼中,首先創(chuàng)建三個 DoubleAdder 對象分別表示統(tǒng)計結(jié)果,然后創(chuàng)建了 30 個商場入口檢測設(shè)備模擬檢測識別,接下來每個檢測設(shè)備如何動作呢,看下面的代碼。
import java.util.Random;
import java.util.concurrent.atomic.DoubleAdder;
public class MonitoringDevice implements Runnable {
private DoubleAdder maleCount;
private DoubleAdder womenCount;
private DoubleAdder unknownGenderCount;
private String monitoringDeviceNo;
public MonitoringDevice(DoubleAdder maleCount, DoubleAdder womenCount, DoubleAdder unknownGenderCount, int monitoringDeviceNo) {
this.maleCount = maleCount;
this.womenCount = womenCount;
this.unknownGenderCount = unknownGenderCount;
this.monitoringDeviceNo = "第" + monitoringDeviceNo + "監(jiān)控采集處";
}
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 0: womenCount.add(1);
System.out.println("統(tǒng)計結(jié)果: womenCount=" + womenCount.sum());
break;
case 1: maleCount.add(1);
System.out.println("統(tǒng)計結(jié)果: maleCount=" + maleCount.sum());
break;
default: unknownGenderCount.add(1);
System.out.println("統(tǒng)計結(jié)果: unknownGenderCount=" + unknownGenderCount.sum());
break;
}
}
}
}
在 MonitoringDevice 類中,首先模擬監(jiān)測設(shè)備輸出,然后將輸出結(jié)果使用 add () 進行統(tǒng)計累加,使用 sum () 輸出累加結(jié)果。運行一段時間后運行結(jié)果如下。
...
統(tǒng)計結(jié)果: unknownGenderCount=23.0
統(tǒng)計結(jié)果: womenCount=24.0
統(tǒng)計結(jié)果: maleCount=32.0
...
上面的案例中,總共計算了三個統(tǒng)計值,每一個統(tǒng)計值都使用了多個線程同時進行統(tǒng)計計算。在統(tǒng)計過程中,每一個線程只需要累加自己的那份統(tǒng)計結(jié)果,所以不需要做同步控制,只要在最后進行匯總統(tǒng)計結(jié)果時做同步控制進行匯總即可。像這樣的場景使用 DoubleAdder 工具類會非常方便簡潔。
至此,大家對 DoubleAdder 已經(jīng)有了初步的理解,接下來我們繼續(xù)豐富對 DoubleAdder 工具類的認識。
6. 核心方法介紹
除過上面代碼中使用的最基本的 add (int)、sum () 方法之外,我們再介紹兩個方法的使用。
- reset () 方法
將累加器值置為 0,即為后繼使用重新歸位。
- sumThenReset () 方法
此方法邏輯等同于先調(diào)用 sum () 方法再調(diào)用 reset () 方法,簡化代碼編寫。
7. 小結(jié)
本節(jié)通過一個簡單的例子,介紹了 DoubleAdder 的基本用法。在 java.util.concurrent.atomic 包中還有一個類似的工具類 LongAdder,用法大同小異,希望大家在日常研發(fā)中多比較多總結(jié),早日掌握之。