利特爾法則派生於排隊論,用如下數學公式表示:java
L = λWide
L 系統中存在的平均請求數量。測試
λ 請求有效到達速率。例如:5/s 表示每秒有5個請求到達系統。this
W 請求在系統中的平均等待執行時間。線程
排隊論:研究服務系統中排隊現象隨機規律的學科,探究排隊有關的數量指標的機率規律性。3d
咱們先假設一個店鋪員工調整場景。code
每一個客戶一次只買一隻炸雞;blog
每位員工製做一個炸雞須要1分鐘。隊列
若是你是一家炸雞店老闆,今年受疫情影響須要對店裏的員工進行調整,你會如何處理?內存
這個問題本質就是員工利用率與客戶體驗之間的權衡。
爲了讓客戶保持極佳體驗,須要保持員工數量或增長員工;
假設店裏目前有3名員工。你如何進行員工調整決策。咱們分析如下幾種情形。
當 平均客流量 = 3人/分鐘 客戶等待時間稍短,體驗良好,而且員工工做都是飽和。此時不須要調整。
當 平均客流量 < 3人/分鐘 客戶等待時間稍短,體驗良好,可是始終有一個員工在打醬油,此時能夠考慮減裁一人。
當 平均客流量 > 3人/分鐘 客戶5,6,7等待時間延長體驗稍差,此時能夠根據實際狀況增長員工。
平均每分鐘客流量 ≈ 員工數 爲最佳。
其實線程池處理也算是一個排隊模型。簡化Java線程池處理模型以下:
線程池任務執行大體階段:提交 --> 入隊列或直接執行 ---> 實際執行
任務提交頻率:每秒任務提交數;
任務隊列等待平均耗時:任務隊列等待總耗時除以實際執行數;
任務實際執行平均耗時:任務實際運行總耗時除以實際執行數;
咱們能夠根據如下指標來評估調整線程池參數
線程池中平均任務數 = 任務提交頻率 * 任務執行平均耗時
線程等待耗時與響應時間比率 = 任務隊列等待總耗時 / (任務隊列等待總耗時 + 任務實際執行總耗時)
當 線程等待耗時與響應時間比率 太高,說明任務排隊較多,評估當前線程池大小是否合理,結合系統負載進行相應調整。
當 線程池中平均任務數 < 目前線程池大小 應適當減小線程數量。
當 系統平均處理任務數 > 目前線程池大小 在這種狀況下,先評估當前系統是否有能力支撐更大的線程數量(如CPU數,內存等),而後再進行調整。
@Slf4j public class MonitoredThreadPoolExecutor extends ThreadPoolExecutor { //任務提交成功時間 private final ConcurrentHashMap<Runnable, Long> timeOfRequest = new ConcurrentHashMap<>(); //任務實際開始執行時間 private final ThreadLocal<Long> startTime = new ThreadLocal<>(); //上一個任務提交成功時間 private long lastArrivalTime; // 任務實際執行總數 private final AtomicInteger numberOfRequestsRetired = new AtomicInteger(); // 任務提交總數 private final AtomicInteger numberOfRequests = new AtomicInteger(); // 任務實際執行總耗時 private final AtomicLong totalServiceTime = new AtomicLong(); // 任務在隊列等待總耗 private final AtomicLong totalPoolTime = new AtomicLong(); // 新任務提交總耗時 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個任務,線程數爲1
兩組迭代請求,一次提交10個任務,線程數爲10
兩組迭代請求,一次提交10個任務,線程數爲50
上面測試比較片面。現實應根據系統長期平均指標進行調整。
利特爾法則應用場景不少。歡迎你們留言交流!