線程池化技術,是通過一個公用的線程隊列,將一個或多個線程進行統一資源調用,可以支持線程資源的重復使用的技術,可以有效的避免因為線程調用創建和銷毀線程過程帶來的資源消耗問題。
為什么要使用線程池?
1、可以通過重復利用創建好的線程來降低線程創建和銷毀造成的資源消耗
2、可以提升系統響應速度,因為核心線程是一直存在的,所以多線程任務不需要等待線程創建就可以立即執行任務。
3、可以利用線程池對線程資源進行統一的管理,避免線程創建過多導致的內存溢出問題等問題。
JAVA中如何來實現線程池的管理的?
Java是從JDK1.5開始的時候,將工作單元與線程執行機制進行了分離來提供線程池的使用管理機制,其中工作單元主要就是包括Runnable和Callable,而線程的執行機制則是交給了executors來提供,代碼如下。
首先我們來創建一個用于測試的線程類。
public class TestThread implements Runnable {private String count;public TestThread(String count) {this.count = count;@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+" 開始計數 Count = "+count);executeCount();System.out.println(Thread.currentThread().getName()+" 結束計數");private void executeCount() {try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();@Overridepublic String toString(){return this.count;
創建測試主類
public class SimpleTheadPool {public static void main(String[] args) {// 創建線程池ExecutorService executorService = Executors.newFixedThreadPool(5);//創建執行線程操作for (int i = 0; i < 10; i++) {Runnable testThread = new TestThread(String.valueOf(i));executorService.execute(testThread);executorService.shutdown();while (executorService.isTerminated()){System.out.println("完成所有線程");
在測試類中,創建了一個固定大小的線程池,并且給線程池分配了10個需要執行的測試任務,因為線程池的初始大小是5,所以無法一次容納10個線程的處理,所以當線程進入之后,會有一部分線程進入到等待狀態,當前面進入的線程執行完成之后,后續的線程就會自動進入執行,執行效果如下圖所示。
從執行結果來看,線程池中只存在了五個線程,并且這五個線程并不會隨著工作執行完成而銷毀,會一直等待分配線程執行任務,一直到線程池調用了銷毀函數來進行銷毀。
Executors使用了ExecutorService提供的線程實現,并且newFixedThreadPool() 方法創建了如下的一個線程池類。
public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue());
下面我們就來看看ThreadPoolExecutor線程池類
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {// 省略代碼
corePoolSize:核心線程數,當我們往線程池中添加一個任務之后,線程池會創建一個新的線程去執行任務,當創建的線程數等于corePoolSize之后。繼續提交任務就會被阻塞到隊列中,等待執行。當然如果調用了prestartAllCoreThreads()方法,線程池也會啟動所有的核心線程。
workQueue:用來保存等待執行任務的阻塞隊列。
maximumPoolSize:線程池中被允許的最大線程數。當等待執行阻塞隊列裝滿之后,如果繼續提交任務,則會繼續創建線程去執行任務,前提是創建的線程數要小于maximumPoolSize值。這里需要注意的是如果隊列可以存放的任務是無限的,那么這個參數就不會起作用,因為隊列會一直存放等待線程。
keepAliveTime:線程空閑等待時間,當線程沒有需要執行的任務的時候,就會在等待指定時間之后被銷毀,默認情況下只會銷毀超過corePoolSize數的線程。
unit:設置等待的線程時間單位
threadFactory:創建線程的工廠類。通過自定義的方式可以給每個線程創建一個具有識別性的線程名稱,用于后續問題定位。
handler:線程拒絕策略,當隊列滿的時候,如果沒有空閑線程執行任務,那么就需要拒絕策略的參與。
實現方式
newFixedThreadPool
當線程數達到了核心線程數之后,即使沒有可以執行的任務,線程池也不會釋放線程。并且這個線程池采用了一個無界的隊列,也就是說阻塞隊列永遠不會達到飽和。
public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue());
newSingleThreadExecutor
初始化線程池中只有一個線程執行任務,如果這個線程因為異常而結束工作,則會重新創建一個新的線程來繼續執行該任務,這個唯一線程的執行操作就可以保證所有的提交任務都是按照隊列排序順序執行。
public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue()));
newCachedThreadPool
這線程池的線程數可以達到Integer.MAX_VALUE值,并且內部使用了SynchronousQueue隊列來作為阻塞隊列。
public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue());
總結
上面我們介紹了在Java中如何去創建一個線程池,并且介紹了創建線程池相關的核心參數。在后續的分享中還會給大家詳細介紹關于Java中線程池的使用,敬請期待