java線程池01-ThreadPoolExecutor構造方法參數的使用規則


為了更好的使用多線程,JDK提供了線程池供開發人員使用,目的在於減少線程的創建和銷毀次數,以此達到線程的重復利用。

其中ThreadPoolExecutor是線程池中最核心的一個類,我們先簡單看一下這個類的繼承關系。

 其中Executor是線程池的頂級接口,接口中只定義了一個方法  void execute(Runnable command);線程池的操作方法都是定義子在ExecutorService子接口中的,所以說ExecutorService是線程池真正的接口。

ThreadPoolExecutor提供了四個構造方法,我們看一下參數最全的一個構造函數;

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

}

函數的參數含義如下:

  • corePoolSize: 線程池核心線程數
  • maximumPoolSize:線程池最大數
  • keepAliveTime: 空閑線程存活時間
  • unit: 時間單位
  • workQueue: 線程池所使用的緩沖隊列
  • threadFactory:線程池創建線程使用的工廠
  • handler: 線程池對拒絕任務的處理策略

本節我們主要對前五個參數中的corePoolSize,maximumPoolSize及workQueue是如何配合使用做出說明(keepAliveTime,unit主要對空閑線程的存活時間做的定義,見名知意,不再做出說明),以此來引出線程池的一些特性。

threadFactory和handler這兩個參數都有默認值,對於它們的用法將放到其它章節去做說明。

特性一:當池中正在運行的線程數(包括空閑線程)小於corePoolSize時,新建線程執行任務。

下面用實驗來說明,代碼如下:

public class TestThreadPoolExecutor {
    
    public static void main(String[] args) {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 3, 60L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(1));
        //任務1
        pool.execute(new Runnable() {

            @Override
            public void run() {
                System.out.println("-------------helloworld_001---------------" + Thread.currentThread().getName());
            }
        });
        
        try {
            //主線程睡2秒
            Thread.sleep(2*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //任務2
        pool.execute(new Runnable() {

            @Override
            public void run() {
                System.out.println("-------------helloworld_002---------------" + Thread.currentThread().getName());
            }
        });
        
    }

}

實驗結果如下:

實驗結果分析:

從實驗結果上可以看出,當執行任務1的線程(thread-1)執行完成之后,任務2並沒有去復用thread-1而是新建線程(thread-2)去執行任務。

特性二:當池中正在運行的線程數大於等於corePoolSize時,新插入的任務進入workQueue排隊(如果workQueue長度允許),等待空閑線程來執行

下面用實驗來說明,代碼如下:

 

public class TestThreadPoolExecutor {

    public static void main(String[] args) {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1));
        // 任務1
        pool.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3 * 1000);
                    System.out.println("-------------helloworld_001---------------" + Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        // 任務2
        pool.execute(new Runnable() {

            @Override
            public void run() {
                try {
                    Thread.sleep(5 * 1000);
                    System.out.println("-------------helloworld_002---------------" + Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        // 任務3
        pool.execute(new Runnable() {

            @Override
            public void run() {
                System.out.println("-------------helloworld_003---------------" + Thread.currentThread().getName());
            }
        });

    }

}

 實驗結果如下:

實驗結果分析:

從實驗結果上看,任務3會等待任務1執行完之后,有了空閑線程,才會執行。並沒有新建線程執行任務3,這時maximumPoolSize=3這個參數不起作用

特性三:當隊列里的任務數達到上限,並且池中正在運行的線程數小於maximumPoolSize對於新加入的任務,新建線程。

下面用實驗來說明,代碼如下:

public class TestThreadPoolExecutor {

    public static void main(String[] args) {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1));
        // 任務1
        pool.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3 * 1000);
                    System.out.println("-------------helloworld_001---------------" + Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        // 任務2
        pool.execute(new Runnable() {

            @Override
            public void run() {
                try {
                    Thread.sleep(5 * 1000);
                    System.out.println("-------------helloworld_002---------------" + Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        // 任務3
        pool.execute(new Runnable() {

            @Override
            public void run() {
                System.out.println("-------------helloworld_003---------------" + Thread.currentThread().getName());
            }
        });

        // 任務4
        pool.execute(new Runnable() {

            @Override
            public void run() {
                System.out.println("-------------helloworld_004---------------" + Thread.currentThread().getName());
            }
        });

    }

}

實驗結果如下:

實驗結果分析:

任務4進入隊列時發現隊列的長度已經到了上限,所以無法進入隊列排隊,而此時正在運行的線程數(2)小於maximumPoolSize所以新建線程執行該任務。

特性四:當隊列里的任務數達到上限,並且池中正在運行的線程數等於maximumPoolSize對於新加入的任務,執行拒絕策略(線程池默認的拒絕策略是拋異常)。

下面用實驗來說明,代碼如下:

public class TestThreadPoolExecutor {

    public static void main(String[] args) {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(2, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1));
        // 任務1
        pool.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3 * 1000);
                    System.out.println("-------------helloworld_001---------------" + Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        // 任務2
        pool.execute(new Runnable() {

            @Override
            public void run() {
                try {
                    Thread.sleep(5 * 1000);
                    System.out.println("-------------helloworld_002---------------" + Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        // 任務3
        pool.execute(new Runnable() {

            @Override
            public void run() {
                System.out.println("-------------helloworld_003---------------" + Thread.currentThread().getName());
            }
        });

        // 任務4
        pool.execute(new Runnable() {

            @Override
            public void run() {
                try {
                    Thread.sleep(2 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("-------------helloworld_004---------------" + Thread.currentThread().getName());
            }
        });

        // 任務5
        pool.execute(new Runnable() {

            @Override
            public void run() {
                System.out.println("-------------helloworld_005---------------" + Thread.currentThread().getName());
            }
        });

    }

}

實驗結果如下:

實驗結果分析:

任務5加入時,隊列達到上限,池內運行的線程數達到最大,故執行默認的拒絕策略,拋異常。

本文中使用到的隊列類型雖然僅限於LinkedBlockingQueue這一種隊列類型,但總結出來的特性,對與常用ArrayBlockingQueue 和 SynchronousQueue同樣適用,些許不同及三種隊列的區別,將在下個章節中說明。

最后說一點,我們作為程序員,研究問題還是要仔細深入一點的。當你對原理了解的有夠透徹,開發起來也就得心應手了,很多開發中的問題和疑惑也就迎刃而解了,而且在面對其他問題的時候也可做到觸類旁通。當然在開發中沒有太多的時間讓你去研究原理,開發中要以實現功能為前提,可等項目上線的后,你有大把的時間或者空余的時間,你大可去刨根問底,深入的去研究一項技術,為覺得這對一名程序員的成長是很重要的事情。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM