Phaser性能測試加強版

早前的舊文中,我分享了使用 java.util.concurrent.Phaser 在處理大量異步任務場景下的使用。其中用到了phaser類的重要特性 可以靈活設置同步數量,在使用過程中註冊新的同步對象。

但是在後續的使用過程中遇到的一些問題,主要有一下兩點:

  1. 註冊同步等待總量有上限 private static final int MAX_PARTIES = 0xffff;
  2. 功能複雜,API豐富但大部分用不到,在使用過程中經常調錯API

今天終於無法忍受,特別是低一點,導致大量異步任務會丟數據。之前是按照非同步方式執行大量任務,但是今天遇到了不定任務量,一時沒想到這茬,導致了半個小時數據構造化爲泡影。

重寫思路

一怒之下,決定自己重寫一個加強版。總結了設計思路如下:

  1. 線程安全計數,用於統計完成任務數
  2. 線程安全狀態技術,用於統計多少任務尚未完成
  3. 註冊方法,用於增加任務統計技術
  4. 完成方法,用於減少未完成數量,增加完成任務數量
  5. 返回各類狀態信息的方法

實現這樣的功能,我們就得到了一個簡單但加強功能的多線程同步類,用來替代 java.util.concurrent.Phaser ,我命名爲 FunPhaser 。代碼如下:

package com.funtester.frame  
  
import java.util.concurrent.atomic.AtomicInteger  
  
/**  
 * 自定義同步類,避免{@link java.util.concurrent.Phaser}的不足,總數量受限於65535  
 * 用於多線程任務同步,任務完成後,調用{@link #done()}方法,任務總數減少,當任務總數爲0時,調用{@link #await()}方法,等待所有任務完成  
 */  
class FunPhaser extends SourceCode {  
  
    /**  
     * 任務總數索引,用於標記任務完成狀態  
     * 註冊增加,任務完成減少  
     */  
    AtomicInteger index  
  
    /**  
     * 任務總數,用於記錄任務完成數量  
     */  
    AtomicInteger taskNum  
  
    FunPhaser() {  
        this.index = new AtomicInteger()  
        this.taskNum = new AtomicInteger()  
    }  
  
    /**  
     * 註冊任務  
     * @return  
     */  
    def register() {  
        this.index.getAndIncrement()  
    }  
  
    /**  
     * 任務完成  
     * @return  
     */  
    def done() {  
        this.index.getAndDecrement()  
        this.taskNum.getAndIncrement()  
    }  
  
    /**  
     * 等待所有任務完成  
     * @return  
     */  
    def await() {  
        waitFor {index.get() == 0}  
    }  
  
    /**  
     * 獲取任務完成總數  
     * @return  
     */  
    int queryTaskNum() {  
        return taskNum.get()  
    }  
  
}

源碼解讀

這個自定義同步類 FunPhaser 用於多線程任務同步,它避免了 java.util.concurrent.Phaser 的不足,即總數量受限於 65535。

FunPhaser 類有以下幾個成員變量:

  • index:任務總數索引,用於標記任務完成狀態。註冊增加,任務完成減少。
  • taskNum:任務總數,用於記錄任務完成數量。

FunPhaser 類提供了以下幾個方法:

  1. indextaskNumAtomicInteger 類型的屬性,用於原子地操作整數值。
  2. FunPhaser() 是該類的構造函數,初始化了 indextaskNum 屬性。
  3. register() 方法用於註冊任務,每次調用會增加 index 的值,表示新增一個任務。
  4. done() 方法用於標記任務完成,每次調用會減少 index 的值並增加 taskNum 的值。
  5. await() 方法用於等待所有任務完成。它調用了 waitFor 方法,等待 index 的值變爲 0,表示所有任務已經完成。
  6. queryTaskNum() 方法用於獲取任務完成的總數,返回 taskNum 的值。

演示Demo

FunPhaser 類的使用方法如下:

import com.funtester.frame.FunPhaser;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class FunPhaserDemo {

    public static void main(String[] args) throws InterruptedException {
        // 創建FunPhaser實例
        FunPhaser phaser = new FunPhaser();

        // 創建固定大小的線程池
        ExecutorService executorService = Executors.newFixedThreadPool(5);

        // 註冊10個任務
        for (int i = 0; i < 10; i++) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    // 註冊任務
                    phaser.register();

                    // 模擬耗時操作
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    // 標記任務完成
                    phaser.done();
                }
            });
        }

        // 等待所有任務完成
        phaser.await();

        // 輸出已完成的任務數量
        System.out.println("已完成的任務數量: " + phaser.queryTaskNum());

        // 關閉線程池
        executorService.shutdown();
    }
}

在這個示例中,我們創建了一個FunPhaser對象,並使用固定大小爲5的線程池來執行10個異步任務。每個任務在開始前調用register()方法註冊,完成後調用done()方法標記。主線程通過調用await()方法等待所有任務完成,並最終輸出已完成的任務數量。

自定義關鍵字

在自定義關鍵字中的使用如下:

/**  
 * 使用自定義同步器{@link FunPhaser}進行多線程同步  
 *  
 * @param f      代碼塊  
 * @param phaser 同步器  
 */  
public static void fun(Closure f, FunPhaser phaser) {  
    if (phaser != null) phaser.register();  
    ThreadPoolUtil.executeSync(() -> {  
        try {  
            f.call();  
        } finally {  
            if (phaser != null) {  
                phaser.done();  
                logger.info("async task {}", phaser.queryTaskNum());  
            }  
        }  
    });  
}

作爲對照舊的實現代碼如下:

/**  
 * 異步執行代碼塊,使用{@link Phaser}進行多線程同步  
 *  
 * @param f      代碼塊  
 * @param phaser 同步器  
 */  
public static void fun(Closure f, Phaser phaser) {  
    if (phaser != null) phaser.register();  
    ThreadPoolUtil.executeSync(() -> {  
        try {  
            f.call();  
        } finally {  
            if (phaser != null) {  
                phaser.arrive();  
                logger.info("異步任務完成 {}", phaser.getArrivedParties());  
            }  
        }  
    });  
}

這兩個實現代碼的功能都是相同的,都是使用同步器來進行多線程任務同步。

舊的實現代碼使用的是 Phaser 類。Phaser 類是一個通用的同步器,可以用於各種多線程任務同步場景。在舊的實現代碼中,我們使用 register() 方法來註冊任務,使用 arrive() 方法來表示任務完成。

新的實現代碼使用的是 FunPhaser 類。FunPhaser 類是一個自定義的同步器,它避免了 Phaser 類的不足,即總數量受限於 65535。在新的實現代碼中,我們使用 register() 方法來註冊任務,使用 done() 方法來表示任務完成。

兩種實現代碼的對比

指標 舊的實現代碼 新的實現代碼
使用到的同步器 Phaser FunPhaser
總數量是否受限
代碼簡潔程度 較好 更好

總體而言,新的實現代碼比舊的實現代碼更加簡潔易用,並且避免了 Phaser 類的不足。

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