文章目錄

  • 前言
  • 一、池的概念
  • 二、線程池是什麼
  • 三、JDK中線程池的使用
  • 3.1 線程池的核心父接口 ExecutorService接口
  • 3.2 Executors =》 線程池的工具類
  • 3.3 ThreadPoolExector子類的核心構造方法參數
  • 四、線程池的工作流程:
  • 拓展

一、池的概念

在引入線程池之前,創建線程有三種方式: 繼承Thread類,實現Runnable接口,實現Callable接口

最終啓動線程都是通過Thread類的start方法,調用Thread類的構造方法產生Thread對象後,線程的狀態處於 new

所謂的new狀態,就是操作系統要準備重新開啓一個線程

調用start方法後,線程的狀態變為 runnable狀態

啓動線程之後,JVM調用線程的run方法來執行線程任務,當run方法執行結束之後,線程的狀態變為
Terminated狀態

雖然創建和銷燬線程開銷比較小(和進程相比),但當系統中線程數量比較多時,這個開銷就比較可觀了。

“池”:目的就是讓某些對象被多次重複利用,減少頻繁創建和銷燬對象帶來的開銷問題(這些對象一定是可以複用的)

舉個栗子:

數據庫連接池,創建和銷燬數據庫的連接就是一個比較耗時的操作,每當一個連接調用close方法終止後,表示當前用户不再使用此連接,就回收連接到連接池中而不是直接銷燬(同一個連接可以被多個用户使用多次,減少了每次創建連接和銷燬連接的系統開銷)

同樣的,不同線程只是run方法的內容不同,線程的大致流程都是一樣的,因此為了避免重複創建和銷燬線程帶來的開銷,可以讓線程“複用”起來。

二、線程池是什麼

內部創建好了若干個線程,這些線程都是runnable,只需要從系統中曲中任務(run),就可以立即開始執行。

舉個栗子:

一個餐廳要營業,假如固定員工線程池中的線程,當固定員工不夠了,就需要臨時產生新員工。假如餐廳十點開門,若此時沒有固定員工,那麼每次點餐都要去臨時招聘臨時工,就需要等待線程創建,當點餐結束,還需要解僱臨時工。 如果有固定員工的話,就不用等待了,直接立即服務。

線程池最大的好處就是減少每次啓動和銷燬線程的損耗(提高時間和空間利用率)。

三、JDK中線程池的使用

描述線程池的核心類,最常用的一個子類- ThreadPoolExecutor,這個類的構造方法就是創建一個線程池的所有核心參數

3.1 線程池的核心父接口 ExecutorService接口

主要方法如下:

1.提交一個任務(線程的run或者call方法)到線程池,線程池就會派遣空閒的線程執行任務。

resin 線程池設置_#intellij-idea

2.立即嘗試終止所有線程池中的線程(無論是否空閒)

resin 線程池設置_#java_02

3.停止所有處在空閒狀態的線程,其他正在執行任務的線程,等待任務執行結束再停止。

resin 線程池設置_#開發語言_03

3.2 Executors =》 線程池的工具類

使用這個類就可以創建JDK內置的四大線程池

Java中類的命名規律 凡是類s =》 工具類 Arrays(數組工具類,copyOf,sort等等)

Executors 創建線程池的幾種方式:

  • newFixedThreadPool: 創建固定線程數的線程池
  • newCachedThreadPool: 創建線程數目動態增長的線程池.
  • newSingleThreadExecutor: 創建只包含單個線程的線程池.
  • newScheduledThreadPool: 設定 延遲時間後執行命令,或者定期執行命令. 是進階版的 Timer.

Executors 本質上是 ThreadPoolExecutor 類的封裝.

完整代碼:(測試)

public class ThreadPoolTest {
    public static void main(String[] args) {
        // 固定大小線程池
        ExecutorService pool = Executors.newFixedThreadPool(10);
        // 數量動態變化的緩存池
        ExecutorService pool1 = Executors.newCachedThreadPool();
        // 只包含一個線程的單線程池
        ExecutorService pool2 = Executors.newSingleThreadExecutor();
        // 定期線程池,可以設置任務的延時啓動時間(Timer類的線程池實現)
        // 定時器線程池
        ScheduledExecutorService pool3 = Executors.newScheduledThreadPool(10);
        pool.submit(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println(Thread.currentThread().getName() + "hello~" + i);
                }
            }
        });
        pool3.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "3s後執行此任務");
            }
        },3, TimeUnit.SECONDS);
    }
}

resin 線程池設置_#java-ee_04

3.3 ThreadPoolExector子類的核心構造方法參數

參數如下(六個):

resin 線程池設置_#開發語言_05

  • corePoolSize ,核心池的線程數量 - 正式員工數
  • maximumPoolSize,線程池的最大線程數量 - 正式工+臨時工
  • keepAliveTime,臨時線程空閒以unit為單位空閒keepAliveTime後銷燬
  • unit,存活時間參數的時間單位
  • workQueue,阻塞隊列,任務都在隊列中存儲,線程從隊列中取出要執行的任務
  • handler,拒絕策略,當任務數量超出線程的負荷時,實行的策略

拒絕策略又如下:

  • AbortPolicy:超出負荷的任務直接拒絕,拋出異常
  • CallerRunsPolicy:返回給線程池的調用者處理
  • DiscardOldestPolicy:丟棄隊列中最老的任務(排隊時間最長的任務)
  • DisCardPolicy:丟棄新來的任務

四、線程池的工作流程:

如圖所示:

resin 線程池設置_#開發語言_06

拓展

阿里編碼規約:儘量不要使用內置線程池,最好根據實際的業務需求,定製線程池自己的new ThreadPoolExecutor對象,傳遞相應的參數!