【揭秘】ThreadPoolExecutor全面解析

2024年2月6日 18点热度 0人点赞

【揭秘】ThreadPoolExecutor全面解析 - 程序員古德

內容摘要

ThreadPoolExecutor線程池通過資源復用、負載均衡、並發控制、異步處理和靈活擴展等方式,提高了系統性能、響應速度和滿足了實際需求的調整。

核心概念

ThreadPoolExecutor是Java中強大的線程管理工具,它能夠高效地復用線程,顯著降低資源消耗與響應延遲,提升系統吞吐量,通過靈活配置核心與最大線程數、隊列容量等參數,ThreadPoolExecutor可輕松應對不同並發場景,確保任務平穩運行。

ThreadPoolExecutor提供了對多線程更細粒度的控制,接下來模擬一個例子,假如,有一個電商平臺,在雙11這樣的大促活動時,需要處理比平常多出數倍的訂單,如果每個訂單都單獨用一個線程處理,那麼系統很快就會因為線程過多而崩潰,這就好比一條擁堵不堪的道路,每輛車(線程)都在緩慢移動,整體效率極低。

ThreadPoolExecutor就像是一個高效的交通指揮中心,可以預設一定數量的“車道”(線程),這些“車道”可以並行處理多個訂單(任務),當新的訂單來臨時,如果“車道”有空閑,就立即分配一個“車道”給這個訂單;如果所有“車道”都在忙碌,新的訂單就進入等待區,等待有“車道”空閑出來。

通過合理配置ThreadPoolExecutor的參數,比如核心線程數、最大線程數、隊列容量等,可以讓這個“交通指揮中心”更加智能和高效。比如,在訂單高峰期,可以臨時增加“車道”數量(線程數)以應對壓力;在訂單量較少的時候,則可以減少“車道”數量,以節省系統資源。

使用案例

下面是一個簡單的ThreadPoolExecutor使用案例,如下代碼:

import java.util.concurrent.ArrayBlockingQueue;  
import java.util.concurrent.ExecutorService;  
import java.util.concurrent.ThreadPoolExecutor;  
import java.util.concurrent.TimeUnit;  
public class ThreadPoolExecutorExample {  
    public static void main(String[] args) {  
        // 創建一個具有固定大小的線程池  
        int corePoolSize = 2; // 核心線程數  
        int maximumPoolSize = 4; // 最大線程數  
        long keepAliveTime = 10; // 空閑線程存活時間  
        ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(2); // 任務隊列  
        // 創建 ThreadPoolExecutor 實例  
        ThreadPoolExecutor executor = new ThreadPoolExecutor(  
                corePoolSize,  
                maximumPoolSize,  
                keepAliveTime,  
                TimeUnit.SECONDS,  
                queue  
        );  
        // 提交任務到線程池  
        for (int i = 0; i < 6; i  ) {  
            final int taskId = i;  
            executor.submit(() -> {  
                System.out.println("Task "   taskId   " is running on thread "   Thread.currentThread().getName());  
                try {  
                    // 模擬任務執行時間  
                    Thread.sleep(1000);  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
            });  
        }  
    }  
}

在上面的代碼中,創建了一個ThreadPoolExecutor實例,設置了核心線程數為2,最大線程數為4,並且使用了一個容量為2的任務隊列,然後提交了6個任務到線程池,由於任務隊列和核心線程數之和(2 2)小於提交的任務數(6),因此一些任務需要等待,直到有線程變得可用。

執行這段代碼,會輸出如下內容:

Task 0 is running on thread pool-1-thread-1  
Task 1 is running on thread pool-1-thread-2  
Task 2 is running on thread pool-1-thread-1  
Task 3 is running on thread pool-1-thread-2  
Task 4 is running on thread pool-1-thread-1  
Task 5 is running on thread pool-1-thread-2

核心API

ThreadPoolExecutor 是 Java 並發庫中的一個類,提供了豐富的方法來控制線程池的行為和管理提交的任務,線程池通過重用現有的線程來減少在創建和銷毀線程上所需的時間以及系統資源的開銷,以下是一些關鍵方法的概述:

1、關鍵構造方法

  • ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)
  • ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)
  • ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler)
  • ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)

構造方法用於創建一個新的 ThreadPoolExecutor 實例,各個參數的含義是:

  • corePoolSize:核心線程數,即線程池中最小的線程數量,即使這些線程處於空閑狀態,也不會被回收。
  • maximumPoolSize:線程池允許的最大線程數。
  • keepAliveTime:當線程數超過核心線程數時,多餘空閑線程在終止前等待新任務的最長時間。
  • unit:keepAliveTime 的時間單位。
  • workQueue:用於存儲等待執行的任務的阻塞隊列。
  • threadFactory:用於創建新線程的工廠。
  • handler:當線程池無法執行新任務時(如隊列已滿或線程池已關閉),用於處理這些任務的策略。

2、關鍵方法

  1. **execute(Runnable command)**: 提交一個需要執行的任務,該方法沒有返回值,通常用於執行不需要返回結果的任務。
  2. submit(Runnable task) 和 **submit(Callable task)**: 提交一個返回值的任務用於執行,返回一個表示任務的未決結果的 FutureRunnable 任務返回 null,而 Callable 任務返回其調用的結果。
  3. **shutdown()**: 啟動線程池的關閉,這不會立即終止已提交的任務,但會拒絕所有新任務。
  4. **shutdownNow()**: 嘗試停止所有正在執行的任務,暫停處理正在等待的任務,並返回等待執行的任務列表。
  5. **isShutdown()**: 如果線程池已關閉,則返回 true
  6. **isTerminated()**: 如果所有任務都已完成,線程池的工作線程也已關閉,則返回 true
  7. **awaitTermination(long timeout, TimeUnit unit)**: 請求關閉、發生超時或者當前線程被中斷,無論哪一個首先發生之後,都將導致阻塞,直到所有任務完成執行。
  8. **getPoolSize()**: 返回線程池中的線程數量。
  9. **getActiveCount()**: 返回正在執行任務的線程數量。
  10. **getTaskCount()**: 返回線程池大約執行過的任務總數。
  11. **getCompletedTaskCount()**: 返回線程池已完成的任務總數,該值小於或等於 getTaskCount() 方法返回的值。
  12. **prestartAllCoreThreads()**: 啟動所有核心線程,使它們等待工作隊列中的任務。
  13. **prestartCoreThread()**: 啟動一個核心線程,如果還沒有核心線程啟動或者可以啟動的話。
  14. allowsCoreThreadTimeOut() 和 **setAllowsCoreThreadTimeOut(boolean value)**: 這些方法用於確定和設置核心線程是否會在空閑時超時。
  15. getCorePoolSize(), setMaximumPoolSize(int maximumPoolSize), getKeepAliveTime(TimeUnit unit), getThreadFactory(), getRejectedExecutionHandler() 等等: 這些方法用於獲取或設置線程池的各種配置參數。

註意,線程池的配置對其行為至關重要,配置不當可能會導致性能問題、資源耗盡或任務被拒絕,因此,在使用 ThreadPoolExecutor 時,務必仔細考慮這些配置參數。

核心總結

【揭秘】ScheduledThreadPoolExecutor全面解析 - 程序員古德

ThreadPoolExecutor提供了線程池的實現,其優點主要表現在:

  1. 資源復用:通過線程池,可以復用已存在的線程,避免頻繁地創建和銷毀線程,降低了開銷。
  2. 提高性能:線程池中的線程是預先創建的,能快速響應請求,提高系統的吞吐量。
  3. 任務調度:ThreadPoolExecutor提供了多種調度策略,如優先隊列、輪詢等,以滿足不同的任務需求。

它的缺點也是非常明顯的,如下:

  1. 無法充分利用多核資源:如果線程數量設置得較少,可能無法充分利用多核CPU資源。
  2. 難以管理:如果線程池參數設置不當,如線程數過多,可能導致系統資源耗盡;反之,則可能無法充分發揮系統性能。

因此在使用時,需要根據實際需求和系統資源,設置合適的核心線程數,並為ThreadPoolExecutor設置合適的拒絕策略,以處理無法執行的任務。並且要定期監控線程池的狀態,根據實際運行情況對線程池參數進行調優。

關註我,每天學習互聯網編程技術 - 程序員古德