天天看點

線程池的簡單實作:Java線程池初學者必讀指南

作者:ggball的blog
"作為一名Java開發者,是否曾經遇到過多線程并發的問題?線程數量過多時,會導緻資源浪費,應用性能下降,甚至發生線程死鎖的情況。那麼,有沒有一種方法可以有效地管理線程,避免這些問題呢?答案是肯定的,那就是線程池。在本文中,我們将通過Java的角度,探讨線程池的奧妙,深入了解線程池的優勢,學習如何使用線程池實作多線程并發。"

線程池是如何建立的?

JAVA中建立線程池主要有兩類方法,一類是通過Executors工廠類提供的方法,該類提供了4種不同的線程池可供使用。另一類是通過ThreadPoolExecutor類進行自定義建立。

Executors工廠類

// 五種線程池:
//     ExecutorService threadPool = null;
//     threadPool = Executors.newCachedThreadPool();//有緩沖的線程池,線程數 JVM 控制
//     threadPool = Executors.newFixedThreadPool(3);//固定大小的線程池
//     threadPool = Executors.newScheduledThreadPool(2); // 具有延時,定時功能
//     threadPool = Executors.newSingleThreadExecutor();//單線程的線程池,隻有一個線程在工作
//     threadPool = new ThreadPoolExecutor();//預設線程池,可控制參數比較多   

private static void createCachedThreadPool() {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            final int index = i;
            executorService.execute(() -> {
                // 擷取線程名稱,預設格式:pool-1-thread-1
                System.out.println(DateUtil.now() + " " + Thread.currentThread().getName() + " " + index);
                // 等待2秒
                sleep(2000);
            });
        }
    }
           

ThreadPoolExecutor類

ThreadPoolExecutor提供構造方法,需要自己設定具體的參數,更加靈活

public ThreadPoolExecutor(int corePoolSize, // 核心線程數
                              int maximumPoolSize, // 最大工作線程
                              long keepAliveTime, // 存活時間,線程沒有任務執行時最多保持多久時間會終止。
                              TimeUnit unit, // 時間機關
                              BlockingQueue<Runnable> workQueue, // 工作隊列
                              ThreadFactory threadFactory, // 線程工廠,主要用來建立線程,默及正常優先級、非守護線程。
                              RejectedExecutionHandler handler // 拒絕政策,當建立新線程使線程數大于最大線程的情況下,會執行
                              ) {
        // 省略...
    }
           

線程池的主要參數

  • corePoolSize 核心線程數 核心線程數,線程池中始終存活的線程數。
  • BlockingQueue 工作隊列 workQueue = new ArrayBlockingQueue<>(5);//基于數組的先進先出隊列,有界 workQueue = new LinkedBlockingQueue<>();//基于連結清單的先進先出隊列,無界 workQueue = new SynchronousQueue<>();//無緩沖的等待隊列,無界
  • threadFactory 線程工廠 用于設定建立線程的工廠,可以通過線程工廠給每個建立出來的。線程設定更有意義的名字。使用開源架構 guava 提供的 ThreadFactoryBuilder 可以快速給線程池裡的線程設定有意義的名字,代碼如下:
new ThreadFactoryBuilder().setNameFormat("XX-task-%d").build();
           
  • handler 拒絕政策,拒絕處理任務時的政策,4種可選,預設為AbortPolicy。

參數 描述 AbortPolicy 拒絕并抛出異常 CallerRunsPolicy 隻用調用者所線上程來運作任務 DiscardOldestPolicy 抛棄隊列頭部(最舊)的一個任務,并執行目前任務。 DiscardPolicy 抛棄目前任務。

RejectedExecutionHandler rejected = null;
    rejected = new ThreadPoolExecutor.AbortPolicy();//預設,隊列滿了丢任務抛出異常
    rejected = new ThreadPoolExecutor.DiscardPolicy();//隊列滿了丢任務不異常
    rejected = new ThreadPoolExecutor.DiscardOldestPolicy();//将最早進入隊列的任務删,之後再嘗試加入隊列
    rejected = new ThreadPoolExecutor.CallerRunsPolicy();//如果添加到線程池失敗,那麼主線程會自己去執行
           

線程池的執行過程?

線程池的簡單實作:Java線程池初學者必讀指南
  1. 主線程送出任務到線程池,如果目前線程數小于核心線程數,建立新的線程用于執行任務,如果不是,下一步。
  2. 此時核心線程已滿,再判斷工作隊列存放的線程數是否滿了,如果沒有滿,則放入工作隊列,如果不是,下一步。
  3. 此時工作隊列滿了,再看目前線程數是否等于最大線程數,如果是的話,執行拒絕政策,如果不是,建立新的線程,執行任務。

配置線程池最大線程數

  • cpu密集型 maximumPoolSize = n*cpu + 1
  • io密集型 maximumPoolSize = 2 * n * cpu

線程池的關閉

可以通過調用線程池的 shutdown 或 shutdownNow 方法來關閉線程池。 相同點:周遊所有的工作線程,然後interrupt掉線程 不同點:shutdown 調用後,不再接受新的任務,但是會等待正在運作的線程,停止沒有執行任務的線程。 shutdownNow 調用後會嘗試停止正在運作或暫停任務的線程。

原創不易,麻煩點個贊再走呗!