淺談Java ThreadPoolExecutor的使用

一、前言

線程池主要由以下4個核心組件組成。

  • 線程池管理器:用於創建並管理線程池
  • 工作線程:線程池中執行具體任務的線程
  • 任務接口:用於定義工作線程的調度和執行策略,隻有線程實現瞭該接口,線程中的任務才能被線程池調度
  • 任務隊列:放待處理的任務,新的任務將會不斷被加入隊列中,執行完成的任務將從隊列中移除

在這裡插入圖片描述

二、ThreadPoolExecutor

如下是線程池的構造方法

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
}

其中具體參數含義為:

1.corePoolSize:線程池中核心線程的數量

2.maximumPoolSize:線程池中最大線程的數量

3.keepAliveTime:當線程數量超過corePoolSize時,空閑線程的存活時間

4.unit:keepAliveTime的時間單位

5.workQueue:任務隊列,被提交但尚未被執行的任務存放的地方

6.threadFactory:線程工廠,用於創建線程,可使用默認的線程工廠或自定義線程工廠

7.handler:由於任務過多或其他原因導致線程池無法處理時的任務拒絕策略

三、構造函數參數解析

編寫測試類如下:

public class ThreadPoolSerialTest {
    public static void main(String[] args) {
        //核心線程數
        int corePoolSize = 2;
        //最大線程數
        int maximumPoolSize = 4;
        //超過corePoolSize線程數量的線程最大空閑時間
        long keepAliveTime = 2;
        //以秒為時間單位
        TimeUnit unit = TimeUnit.SECONDS;
        //創建工作隊列,用於存放提交的等待執行任務
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(2);
        ThreadPoolExecutor threadPoolExecutor = null;

        try {
            // 1.創建線程池
            threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,
                    maximumPoolSize,
                    keepAliveTime,
                    unit,
                    workQueue,
                    new ThreadPoolExecutor.AbortPolicy());
            // 2.循環提交任務
            for (int i = 0; i < 6; i++) {
                //提交任務的索引
                final int index = (i+1);
                threadPoolExecutor.submit(()->{
                    //線程打印輸出
                    System.out.println("大傢好,我是線程:"+index);
                    try {
                        //模擬線程執行時間,10s
                        Thread.sleep(10000);
                        System.out.println("線程:"+index+"運行完畢");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
                //每個任務提交後休眠500ms再提交下一個任務,用於保證提交順序
                Thread.sleep(500);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 3.關閉線程池
            threadPoolExecutor.shutdown();
        }
    }
}

其中循環瞭6次,讓線程池執行瞭6次任務,恰好滿足maximumPoolSize+workQueue容量=並發執行任務數。輸出結果如下:

大傢好,我是線程:1
大傢好,我是線程:2
大傢好,我是線程:5
大傢好,我是線程:6
線程:1運行完畢
大傢好,我是線程:3
線程:2運行完畢
大傢好,我是線程:4
線程:5運行完畢
線程:6運行完畢
線程:3運行完畢
線程:4運行完畢

這段輸出看似沒有規律,其實這裡輸出完全是由線程池控制的;下面就來分行解析輸出:

大傢好,我是線程:1
大傢好,我是線程:2
大傢好,我是線程:5
大傢好,我是線程:6

1.全新線程池被創建後,有Runnable或CallBack接口的實現被提交給線程池執行;線程池的corePoolSize=2,此時前兩個任務提交後就立即執行,便輸出瞭線程1 線程2

2.此時仍繼續向線程池提交任務,線程池中workQueue容量=2,被加入的任務存放到任務隊列中,即把線程3 線程4存放到瞭任務隊列中;

3.任務隊列充滿後,仍繼續向線程池提交任務,線程池的maximumPoolSize=4,除開核心線程數2個外還允許創建4-2個線程來執行任務,便輸出瞭線程5 線程6

線程:1運行完畢
大傢好,我是線程:3
線程:2運行完畢
大傢好,我是線程:4

1.線程:1運行完畢:表示第一個線程任務執行完畢瞭

2.大傢好,我是線程:3:線程1運行完畢後,此時線程池中有一個空閑的線程,第一個進入任務隊列中的任務第一個交給線程處理

3.線程:2運行完畢 大傢好,我是線程:4 :和上面線程執行完畢,任務對列中任務執行一致

線程:5運行完畢
線程:6運行完畢
線程:3運行完畢
線程:4運行完畢

因為每一個任務的執行時間控制的是一樣的,此時輸出的內容便是先被線程池執行的任務先執行完畢。

四、總結

線程池剛被創建時,隻是向系統申請一個用於執行線程隊列和管理線程池的資源。在調用execute()添加一個任務時,線程池會按照以下流程執行任務:

正在運行的線程數量a:a<corePoolSize,線程池立即創建線程並執行任務;若此時a=corePoolSize,則任務被存放到workQueue任務隊列中,直到任務隊列被充滿

任務隊列workQueue已充滿且正在運行的線程數a:a<maximumPoolSize,線程池立即創建非核心線程並執行任務;若有任務執行完畢,該任務將被線程池隊列中移除,線程池從隊列中取先入隊的任務執行;當線程處於空閑狀態的時間超過keepAliveTime時間時,正在運行的線程數acorePoolSize<a,線程池停止空閑的線程。線程池將任務執行完畢後,線程池會收縮到corePoolSize大小

任務隊列workQueue已充滿且正在運行的線程數a:a=maximumPoolSize,線程池拒絕執行該任務並拋出RejectExecutionException異常

在這裡插入圖片描述

到此這篇關於淺談Java ThreadPoolExecutor的使用的文章就介紹到這瞭,更多相關Java ThreadPoolExecutor內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: