目錄
爲什麼引入Executor線程池框架
① 重用存在的線程,減少對象創建、消亡的開銷,提高性能;
② 線程的創建和運行分開,達到解耦目的;
③ 可有效控制最大併發線程數,提高系統資源的使用率;
Executor原理
Executor生命週期
- RUNNING:可以接收新任務,並且處理阻塞隊列中的任務
- SHUTDOWN:關閉狀態,不能接收新任務,可以繼續處理阻塞隊列中的任務。在線程池處於 RUNNING 狀態時,調用 shutdown()方法會使線程池進入到該狀態。
- STOP:不能接收新任務,也不能處理阻塞隊列中的任務,會中斷正在處理的任務。在線程池處於 RUNNING 或 SHUTDOWN 狀態時,調用 shutdownNow() 方法會使線程池進入到該狀態。
- TIDYING:所有任務都執行完成,線程池中workerCount (有效線程數) 爲0。線程池進入該狀態後會調用 terminated() 方法進入TERMINATED 狀態。
- TERMINATED:調用terminated()方法進入該狀態。
Executor框架介紹與使用
說明:
- Executor 執行器接口,該接口定義執行Runnable任務的方式。
- ExecutorService 該接口定義提供對Executor的服務。
- ScheduledExecutorService 定時調度接口。
- AbstractExecutorService 執行框架抽象類。
- ThreadPoolExecutor JDK中線程池的具體實現。
- Executors 線程池工廠類。
Executor將任務的提交過程與執行過程解耦,並用Runnable來表示任務。執行的任務放入run方法中即可,將Runnable接口的實現類交給線程池的execute方法,作爲它的一個參數。如果需要給任務傳遞參數,可以通過創建一個Runnable接口的實現類來完成。
Executor可以支持多種不同類型的任務執行策略。
Executor基於生產者消費者模式,提交任務的操作相當於生產者,執行任務的線程則相當於消費者。
ThreadPoolExecutor 線程池類
線程池是一個複雜的任務調度工具,它涉及到任務、線程池等的生命週期問題。要配置一個線程池是比較複雜的,尤其是對於線程池的原理不是很清楚的情況下,很有可能配置的線程池不是較優的。
JDK中的線程池均由ThreadPoolExecutor類實現。其構造方法如下:
corePoolSize(線程池的基本大小):當提交一個新的任務給線程池後,會創建一個新的線程來執行任務(當前線程池線程數 < corePoolSize),等到需要執行的任務數大於線程池基本大小大於corePoolSize時就不再創建,而是把任務放進保持的等待隊列
maximumPoolSize(線程池最大大小):線程池允許最大線程數。如果阻塞隊列滿了,並且已經創建的線程數小於最大線程數,則線程池會再創建新的線程執行。因爲線程池執行任務時是線程池基本大小滿了,後續任務進入阻塞隊列,阻塞隊列滿了,在創建線程。
keepAliveTime(線程活動保持時間):空閒的線程保持活動得時間(好像只有在newCachedThreadPool這個方法的參數中才有意義(不爲0))
TimeUnit(線程活動保持時間的單位):
TimeUnit.DAYS; //天
TimeUnit.HOURS; //小時
TimeUnit.MINUTES; //分鐘
TimeUnit.SECONDS; //秒
TimeUnit.MILLISECONDS; //毫秒
TimeUnit.MICROSECONDS; //微妙
TimeUnit.NANOSECONDS; //納秒
workQueue(工作隊列):用於保存待執行的任務的阻塞隊列,有如下幾種:
- ArrayBlockingQueue:數組結構的有界阻塞隊列,先進先出FIFO
- LinkedBlockingQueue:鏈表結構的無界阻塞隊列。先進先出FIFO排序元素,靜態方法Executors.newFixedThreadPool和Executors.newFixedThreadPool.newSingleThreadExecutor使用這個隊列
- SynchronousQueue:不存儲元素的阻塞隊列,就是每次插入操作必須等到另一個線程調用移除操作,靜態方法Executors.newCachedThreadPool使用這個方法
threadFactory:設置創建線程的工廠,通過這個工廠可以給線程一些比較有意義的名字
handler:一種飽和策略,當線程池和隊列都飽滿時拒絕處理任務的策略,默認是AbortPolicy,表示無法處理新的任務時拋出異常;拒絕策略有如下幾種:
-
ThreadPoolExecutor.AbortPolicy:丟棄任務並拋出RejectedExecutionException異常。
-
ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,但是不拋出異常。
-
ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然後重新嘗試執行任務(重複此過程)
-
ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務
ThreadPoolExecutor需要注意以下概念:
-
若線程池中的線程數量小於corePoolSize,即使線程池中的線程都處於空閒狀態,也要創建新的線程來處理被添加的任務。
-
若線程池中的線程數量等於 corePoolSize且緩衝隊列 workQueue未滿,則任務被放入緩衝隊列。
-
若線程池中線程的數量大於corePoolSize且緩衝隊列workQueue滿,且線程池中的數量小於maximumPoolSize,則建新的線程來處理被添加的任務。
-
若線程池中線程的數量大於corePoolSize且緩衝隊列workQueue滿,且線程池中的數量等於maximumPoolSize,那麼通過 handler所指定的策略來處理此任務。
- 當線程池中的線程數量大於corePoolSize時,如果某線程空閒時間超過keepAliveTime,線程將被終止。
ExecutorService接口
線程池接口。ExecutorService在Executor的基礎上增加了一些方法,其中有兩個核心的方法:
Future<?> submit(Runnable task)
<T> Future<T> submit(Callable<T> task)
這兩個方法都是向線程池中提交任務,它們的區別在於Runnable在執行完畢後沒有結果,Callable執行完畢後有一個結果。這在多個線程中傳遞狀態和結果是非常有用的。另外他們的相同點在於都返回一個Future對象。Future對象可以阻塞線程直到運行完畢(獲取結果,如果有的話),也可以取消任務執行,當然也能夠檢測任務是否被取消或者是否執行完畢。
在沒有Future之前我們檢測一個線程是否執行完畢通常使用Thread.join()或者用一個死循環加狀態位來描述線程執行完畢。現在有了更好的方法能夠阻塞線程,檢測任務執行完畢甚至取消執行中或者未開始執行的任務。
Executor接口
Executor是一個線程執行接口。任務執行的主要抽象不是Thead,而是Executor。
public interface Executor{
void executor(Runnable command);
}
Executor將任務的提交過程與執行過程解耦,並用Runnable來表示任務。執行的任務放入run方法中即可,將Runnable接口的實現類交給線程池的execute方法,作爲它的一個參數。如果需要給任務傳遞參數,可以通過創建一個Runnable接口的實現類來完成。
Executor可以支持多種不同類型的任務執行策略。
Executor基於生產者消費者模式,提交任務的操作相當於生產者,執行任務的線程則相當於消費者。
ScheduledExecutorService接口
ScheduledExecutorService描述的功能和Timer/TimerTask類似,解決那些需要任務重複執行的問題。這包括延遲時間一次性執行、延遲時間週期性執行以及固定延遲時間週期性執行等。當然了繼承ExecutorService的ScheduledExecutorService擁有ExecutorService的全部特性。
①:Java類庫可以通過調用Executors的靜態工廠方法來創建線程
public class Executors extends Object
static ExecutorService |
newCachedThreadPool()
創建一個根據需要創建新線程的線程池,但在可用時將重新使用以前構造的線程。 |
static ExecutorService |
newCachedThreadPool(ThreadFactory threadFactory)
創建一個根據需要創建新線程的線程池,但在可用時將重新使用以前構造的線程,並在需要時使用提供的ThreadFactory創建新線程。 |
static ExecutorService |
newFixedThreadPool(int nThreads)
創建一個線程池,該線程池重用固定數量的從共享無界隊列中運行的線程。 |
static ExecutorService |
newFixedThreadPool(int nThreads, ThreadFactory threadFactory)
創建一個線程池,重用固定數量的線程,從共享無界隊列中運行,使用提供的ThreadFactory在需要時創建新線程。 |
static ScheduledExecutorService |
newScheduledThreadPool(int corePoolSize)
創建一個線程池,可以調度命令在給定的延遲之後運行,或定期執行。 |
static ScheduledExecutorService |
newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)
創建一個線程池,可以調度命令在給定的延遲之後運行,或定期執行。 |
②:public interface ExecutorService extends Executor;
boolean |
isShutdown()
如果此執行者已關閉,則返回 |
boolean |
isTerminated()
如果所有任務在關閉後完成,則返回 |
void |
shutdown()
啓動有序關閉,其中先前提交的任務將被執行,但不會接受任何新任務。 |
List<Runnable> |
shutdownNow()
嘗試停止所有主動執行的任務,停止等待任務的處理,並返回正在等待執行的任務列表。 |
<T> Future<T> |
submit(Callable<T> task)
提交值返回任務以執行,並返回代表任務待處理結果的Future。 |
public static void main(String[] args) {
//固定長度的線程池
//ExecutorService pool = Executors.newFixedThreadPool(5);
//將創建一個緩存線程池
ExecutorService pool = Executors.newCachedThreadPool();
//可調度的線程池
//ExecutorService pool = Executors.newScheduledThreadPool(5);
for (int i = 0; i < 10; i++) {
pool.execute(new ThreadDemo1());
}
//停止
pool.shutdown();
}