Java 線程池技術總結


線程池的概念其實也沒有那么深奧,可以簡單的理解為就是一個容器內存放了多個空閑的線程,有新任務需要執行時,就從容器里面取出空閑線程,任務執行完畢后,再歸還給容器。

之所以要使用線程池技術,主要還是因為創建一個新線程的成本比較高,程序底層需要跟操作系統進行交互。當程序中需要創建大量生存期限很短暫的線程時,就需要頻繁的創建和銷毀線程,這對系統的資源消耗,很有可能大於業務處理本身對系統的資源消耗,這就本末倒置了(因為我們之所以使用多線程,最終目的是為了提高業務處理能力)。為了盡可能的解決這種問題,我們就需要降低線程創建和銷毀的頻率,我們就需要使用線程池。

Java 的線程池實現技術其實非常簡單,在真實的企業開發中,99% 的情況下,不會讓你自己編碼實現自定義的線程池,而是應該站在巨人的肩上,調用 Java 官方提供的 API 方法來實現。Java 官方提供的線程池 API 方法,學習和使用都非常簡單,下面我就詳細介紹一下吧。


一、使用 Executors 創建默認線程池

我們可以使用 Executors 中所提供的兩個靜態方法來創建線程池,方法如下:

靜態方法名 說明
static ExecutorService newCachedThreadPool() 創建一個默認的線程池,線程池中線程數量最大為 int 的最大值
static newFixedThreadPool(int nThreads) 創建一個指定最大線程數量的線程池

代碼實現:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

public class MyThreadPoolDemo {
    public static void main(String[] args) throws InterruptedException {

        //創建一個默認的線程池,線程池中線程數量最大為 int 的最大值
        //ExecutorService executorService = Executors.newCachedThreadPool();

        //創建一個最大線程數量為 5 的線程池
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        ThreadPoolExecutor pool = (ThreadPoolExecutor) executorService;

        //打印線程池中線程的數量
        System.out.println(pool.getPoolSize()); //當前線程池中的線程數量為 0

        //向線程池提交一個任務
        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName() + " 執行了");
        });

        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName() + " 執行了");
        });

        System.out.println(pool.getPoolSize()); //當前線程池中的線程數量為 2

        //關閉銷毀線程池
        executorService.shutdown();
    }
}

從上面的代碼可以看出:Java 官方提供的默認線程池,在啟動的時沒有創建空閑線程,當我們向線程池提交任務的時,線程池就會啟動一個線程來執行該任務。等待任務執行完畢以后,並不會銷毀線程,而是歸還到線程池中處於空閑狀態,等待后續新任務的執行。


二、使用 ThreadPoolExecutor 創建自定義線程池

創建 ThreadPoolExecutor 線程池的構造方法參數如下:

第 1 個參數:核心線程數量
第 2 個參數:最大線程數量
第 3 個參數:空閑線程最大存活時間
第 4 個參數:存活時間的單位(分、秒、毫秒 ......)
第 5 個參數:任務隊列
第 6 個參數:創建線程工廠(使用默認工廠即可:Executors.defaultThreadFactory())
第 7 個參數:任務的拒絕策略

任務拒絕策略 說明
ThreadPoolExecutor.AbortPolicy 多余的任務被丟棄並拋出 RejectedExecutionException 異常(默認策略)。
ThreadPoolExecutor.DiscardPolicy 多余的任務被丟棄,但是不拋出異常。這是不推薦的做法。
ThreadPoolExecutor.DiscardOldestPolicy 丟棄隊列中等待最久的任務,然后把當前任務加入隊列中。
ThreadPoolExecutor.CallerRunsPolicy 調用任務的 run() 方法繞過線程池直接執行。

下面進行代碼演示,這里只演示任務拒絕策略為 AbortPolicy 和 CallerRunsPolicy 的代碼,因為這兩種比較常用。


1 使用 ThreadPoolExecutor.AbortPolicy 任務拒絕策略

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolExecutorDemo01 {

    public static void main(String[] args) {
        //核心線程數量為1 ,最大線程池數量為3, 任務隊列容量為1 ,空閑線程的最大存在時間為 20 秒
        ThreadPoolExecutor threadPoolExecutor 
            = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS ,
                                     new ArrayBlockingQueue<>(1) ,
                                     Executors.defaultThreadFactory() ,
                                     new ThreadPoolExecutor.AbortPolicy()) ;

        //當前提交 5 個任務,而該線程池最多可以處理 4 個任務,
        //當我們使用 AbortPolicy 這個任務拒絕策略的時候,就會拋出 RejectedExecutionException 異常
        for(int x = 0 ; x < 5 ; x++) {
            threadPoolExecutor.submit(() -> {
                System.out.println(Thread.currentThread().getName() + "----> 執行了任務");
            });
        }
        
        //關閉銷毀線程池
        threadPoolExecutor.shutdown();
    }
}

/*
可以將以下代碼用 try catch 包裹,處理異常
threadPoolExecutor.submit(() -> {
    System.out.println(Thread.currentThread().getName() + "----> 執行了任務");
});
*/

2 使用 ThreadPoolExecutor.CallerRunsPolicy 任務拒絕策略

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolExecutorDemo02 {

    public static void main(String[] args) {
        //核心線程數量為1 ,最大線程池數量為3, 任務隊列容量為1 ,空閑線程的最大存在時間為 20 秒
        ThreadPoolExecutor threadPoolExecutor 
            = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS ,
                                     new ArrayBlockingQueue<>(1) ,
                                     Executors.defaultThreadFactory() ,
                                     new ThreadPoolExecutor.CallerRunsPolicy()) ;

        //當前提交 5 個任務,而該線程池最多可以處理 4 個任務
        for(int x = 0 ; x < 5 ; x++) {
            threadPoolExecutor.submit(() -> {
                System.out.println(Thread.currentThread().getName() + "----> 執行了任務");
            });
        }
        
        //關閉銷毀線程池
        threadPoolExecutor.shutdown();
    }
}

/*
控制台輸出結果如下:
pool-1-thread-1----> 執行了任務
pool-1-thread-3----> 執行了任務
pool-1-thread-2----> 執行了任務
pool-1-thread-1----> 執行了任務
main----> 執行了任務

通過控制台的輸出結果發現:
第 5 個任務沒有被線程池中的線程執行,而是繞過線程池調用 run 方法,在 main 線程中執行。
*/

到此為止,Java 官方提供的線程池技術,已經介紹完畢。以上只是簡單代碼的演示,實際工作中可根據具體業務需求進行改造。另外在實際工作中,線程池一般情況下都不會進行手動編碼關閉銷毀,線程池的生命周期跟整個系統的生命周期相同。




免責聲明!

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



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