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

爲了更好的使用多線程,JDK提供了線程池供開發人員使用,目的在於減小線程的建立和銷燬次數,以此達到線程的重複利用。java

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

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

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

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

}ThreadFactory threadFactory,

函數的參數含義以下:函數

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

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

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

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

下面用實驗來講明,代碼以下:blog

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一樣適用,些許不一樣及三種隊列的區別,將在下個章節中說明。

最後說一點,咱們做爲程序員,研究問題仍是要仔細深刻一點的。當你對原理了解的有夠透徹,開發起來也就駕輕就熟了,不少開發中的問題和疑惑也就迎刃而解了,並且在面對其餘問題的時候也可作到舉一反三。固然在開發中沒有太多的時間讓你去研究原理,開發中要以實現功能爲前提,可等項目上線的後,你有大把的時間或者空餘的時間,你大可去刨根問底,深刻的去研究一項技術,爲以爲這對一名程序員的成長是很重要的事情。

相關文章
相關標籤/搜索