如何使用利特爾法則調(diào)整線程池大小

利特爾法則

利特爾法則派生于排隊論,用以下數(shù)學(xué)公式表示:
L = λW
L 系統(tǒng)中存在的平均請求數(shù)量。

λ 請求有效到達速率。例如:5/s 表示每秒有5個請求到達系統(tǒng)。

W 請求在系統(tǒng)中的平均等待執(zhí)行時間。

排隊論:研究服務(wù)系統(tǒng)中排隊現(xiàn)象隨機規(guī)律的學(xué)科,探究排隊有關(guān)的數(shù)量指標的概率規(guī)律性。

場景

我們先假設(shè)一個店鋪員工調(diào)整場景。

前提

  • 每個客戶一次只買一只炸雞;

  • 每位員工制作一個炸雞需要1分鐘。

  • 客戶買炸雞時等待時間越短,體驗越好。

如果你是一家炸雞店老板,今年受疫情影響需要對店里的員工進行調(diào)整,你會如何處理?

這個問題本質(zhì)就是員工利用率客戶體驗之間的權(quán)衡。

  1. 為了讓客戶保持極佳體驗,需要保持員工數(shù)量或增加員工;

  2. 為避免資源浪費,控制人力成本,需要裁減空閑員工。

假設(shè)店里目前有3名員工。你如何進行員工調(diào)整決策。我們分析以下幾種情形。

當(dāng) 平均客流量 = 3人/分鐘 客戶等待時間稍短,體驗良好,并且員工工作都是飽和。此時不需要調(diào)整。

=3人/分鐘

當(dāng) 平均客流量 < 3人/分鐘 客戶等待時間稍短,體驗良好,但是始終有一個員工在打醬油,此時可以考慮減裁一人。

< 3人/分鐘

當(dāng) 平均客流量 > 3人/分鐘 客戶5,6,7等待時間延長體驗稍差,此時可以根據(jù)實際情況增加員工。

客流量 > 3人/分鐘

平均每分鐘客流量 ≈ 員工數(shù) 為最佳。

線程池

其實線程池處理也算是一個排隊模型。簡化Java線程池處理模型如下:

線程池任務(wù)執(zhí)行大致階段:提交 --> 入隊列或直接執(zhí)行 ---> 實際執(zhí)行

線程池
  • 任務(wù)提交頻率:每秒任務(wù)提交數(shù);

  • 任務(wù)隊列等待平均耗時:任務(wù)隊列等待總耗時除以實際執(zhí)行數(shù);

  • 任務(wù)實際執(zhí)行平均耗時:任務(wù)實際運行總耗時除以實際執(zhí)行數(shù);

  • 任務(wù)執(zhí)行平均耗時:任務(wù)隊列等待平均耗時加任務(wù)實際執(zhí)行平均耗時;

我們可以根據(jù)以下指標來評估調(diào)整線程池參數(shù)

線程池中平均任務(wù)數(shù) = 任務(wù)提交頻率 * 任務(wù)執(zhí)行平均耗時

線程等待耗時與響應(yīng)時間比率 = 任務(wù)隊列等待總耗時 / (任務(wù)隊列等待總耗時 + 任務(wù)實際執(zhí)行總耗時


當(dāng) 線程等待耗時與響應(yīng)時間比率 過高,說明任務(wù)排隊較多,評估當(dāng)前線程池大小是否合理,結(jié)合系統(tǒng)負載進行相應(yīng)調(diào)整。

當(dāng) 線程池中平均任務(wù)數(shù) < 目前線程池大小 應(yīng)適當(dāng)減少線程數(shù)量。

當(dāng) 系統(tǒng)平均處理任務(wù)數(shù) > 目前線程池大小 在這種情況下,先評估當(dāng)前系統(tǒng)是否有能力支撐更大的線程數(shù)量(如CPU數(shù),內(nèi)存等),然后再進行調(diào)整。

代碼片段

@Slf4j
public class MonitoredThreadPoolExecutor extends ThreadPoolExecutor {

    //任務(wù)提交成功時間
    private final ConcurrentHashMap<Runnable, Long> timeOfRequest = new ConcurrentHashMap<>();
    //任務(wù)實際開始執(zhí)行時間
    private final ThreadLocal<Long> startTime = new ThreadLocal<>();
    //上一個任務(wù)提交成功時間
    private long lastArrivalTime;

    // 任務(wù)實際執(zhí)行總數(shù)
    private final AtomicInteger numberOfRequestsRetired = new AtomicInteger();
    // 任務(wù)提交總數(shù)
    private final AtomicInteger numberOfRequests = new AtomicInteger();
    // 任務(wù)實際執(zhí)行總耗時
    private final AtomicLong totalServiceTime = new AtomicLong();
    // 任務(wù)在隊列等待總耗
    private final AtomicLong totalPoolTime = new AtomicLong();
    // 新任務(wù)提交總耗時
    private final AtomicLong aggregateInterRequestArrivalTime = new AtomicLong();


    public MonitoredThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
                                       BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
    }

    @Override
    protected void beforeExecute(Thread worker, Runnable task) {
        super.beforeExecute(worker, task);
        startTime.set(System.nanoTime());
    }

    @Override
    protected void afterExecute(Runnable task, Throwable t) {
        try {
            long start = startTime.get();
            totalServiceTime.addAndGet(System.nanoTime() - start);
            totalPoolTime.addAndGet(start - timeOfRequest.remove(task));
            numberOfRequestsRetired.incrementAndGet();
        } finally {
            if (null != t) {
                log.error(AppSystem.ERROR_LOG_PREFIX + "線程池處理異常:", Throwables.getRootCause(t));
            }
            super.afterExecute(task, t);
        }
    }

    @Override
    public void execute(Runnable task) {
        long now = System.nanoTime();
        numberOfRequests.incrementAndGet();
        synchronized (this) {
            if (lastArrivalTime != 0L) {
                aggregateInterRequestArrivalTime.addAndGet(now - lastArrivalTime);
            }
            lastArrivalTime = now;
            timeOfRequest.put(task, now);
        }
        super.execute(task);
    }
}

測試

兩組迭代請求,一次提交10個任務(wù),線程數(shù)為1

線程數(shù)1

兩組迭代請求,一次提交10個任務(wù),線程數(shù)為10

線程數(shù)10

兩組迭代請求,一次提交10個任務(wù),線程數(shù)為50

線程數(shù)50

上面測試比較片面?,F(xiàn)實應(yīng)根據(jù)系統(tǒng)長期平均指標進行調(diào)整。

總結(jié)

利特爾法則應(yīng)用場景很多。歡迎大家留言交流!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容