線程池框架Executor(併發編程篇)

目錄

爲什麼引入Executor線程池框架

Executor原理

Executor生命週期

Executor框架介紹與使用

ThreadPoolExecutor 線程池類

ExecutorService接口

Executor接口

ScheduledExecutorService接口


爲什麼引入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()

如果此執行者已關閉,則返回 true 。

boolean isTerminated()

如果所有任務在關閉後完成,則返回 true 。

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();
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章