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