線程池基礎

爲什麼要用線程池?

單線程方式存在以下幾個問題:

  • 線程的工作週期:假設線程創建所需時間爲T1,線程執行任務所需時間爲T2,線程銷燬所需的時間爲T3,往往是T1+T3大於T2,所以如果頻繁的創建線程會損耗過多的額外時間。
  • 如果有任務來了,再去創建線程的話效率比較低,如果從一個池子中可以直接獲取可用的線程,那麼效率會有所提升。所以線程池省去了任務過來要先創建線程的過程,節省了時間,提升了效率。
  • 線程池可以管理和控制線程,因爲線程是稀缺資源,如果無限制的創建,不僅會消耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一的分配,調優和監控。
  • 線程池提供隊列,存放緩衝等待執行的任務。

通過線程池創建線程從調用 API 角度來說分爲兩種,一種是原生的線程池,另外該一種是通過 Java 提供的併發包來創建,後者其實是對原生的線程池創建方式做了一次簡化包裝,讓調用者使用起來更方便,但道理都是一樣的。

線程池的基礎架構

線程池的基礎架構圖:

Executor

Executor是任務的執行者,線程池框架中幾乎所有類都直接或者間接實現Executor接口,它是線程池框架的基礎。Executor提供了一種將“任務提交”與“任務執行”分離開來的機制,它僅提供了一個Executor()方法用來執行已經提交的Runnable任務。

ExecutorService

繼承Executor,它是“執行者服務”接口,它是爲"執行者接口Executor"服務而存在的。準確的地說,ExecutorService提供了"將任務提交給執行者的接口(submit方法)","讓執行者執行任務(invokeAll, invokeAny方法)"的接口等等。

public interface ExecutorService extends Executor {
    /**
     * 啓動一次順序關閉,執行以前提交的任務,但不接受新任務
     */
    void shutdown();
    /**
     * 試圖停止所有正在執行的活動任務,暫停處理正在等待的任務,並返回等待執行的任務列表
     */
    List<Runnable> shutdownNow();
    /**
     * 如果此執行程序已關閉,則返回 true。
     */
    boolean isShutdown();
    /**
     * 如果關閉後所有任務都已完成,則返回 true
     */
    boolean isTerminated();
    /**
     * 請求關閉、發生超時或者當前線程中斷,無論哪一個首先發生之後,都將導致阻塞,直到所有任務完成執行
     */
    boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException;
    /**
     * 提交一個返回值的任務用於執行,返回一個表示任務的未決結果的 Future
     */
    <T> Future<T> submit(Callable<T> task);
    /**
     * 提交一個 Runnable 任務用於執行,並返回一個表示該任務的 Future
     */
    <T> Future<T> submit(Runnable task, T result);
    /**
     * 提交一個 Runnable 任務用於執行,並返回一個表示該任務的 Future
     */
    Future<?> submit(Runnable task);
    /**
     * 執行給定的任務,當所有任務完成時,返回保持任務狀態和結果的 Future 列表
     */
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
        throws InterruptedException;
    /**
     * 執行給定的任務,當所有任務完成或超時期滿時(無論哪個首先發生),返回保持任務狀態和結果的 Future 列表
     */
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
                                  long timeout, TimeUnit unit)
        throws InterruptedException;
    /**
     * 執行給定的任務,如果某個任務已成功完成(也就是未拋出異常),則返回其結果
     */
    <T> T invokeAny(Collection<? extends Callable<T>> tasks)
        throws InterruptedException, ExecutionException;
    /**
     * 執行給定的任務,如果在給定的超時期滿前某個任務已成功完成(也就是未拋出異常),則返回其結果
     */
    <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                    long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

AbstractExecutorService

抽象類,實現ExecutorService接口,爲其提供默認實現。AbstractExecutorService除了實現ExecutorService接口外,還提供了newTaskFor()方法返回一個RunnableFuture,在運行的時候,它將調用底層可調用任務,作爲 Future 任務,它將生成可調用的結果作爲其結果,併爲底層任務提供取消操作。

ScheduledExecutorService

繼承ExcutorService,爲一個“延遲”和“定期執行”的ExecutorService。他提供了一些如下幾個方法安排任務在給定的延時執行或者週期性執行。

// 創建並執行在給定延遲後啓用的 ScheduledFuture。
<V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit)
// 創建並執行在給定延遲後啓用的一次性操作。
ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)
// 創建並執行一個在給定初始延遲後首次啓用的定期操作,後續操作具有給定的週期;
//也就是將在 initialDelay 後開始執行,然後在 initialDelay+period 後執行,接着在 initialDelay + 2 * period 後執行,依此類推。
ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
// 創建並執行一個在給定初始延遲後首次啓用的定期操作,隨後,在每一次執行終止和下一次執行開始之間都存在給定的延遲。
ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)

ThreadPoolExecutor

大名鼎鼎的線程池,後續對其做詳細總結。

ScheduledThreadPoolExecutor 

ScheduledThreadPoolExecutor繼承ThreadPoolExecutor並且實現ScheduledExecutorService接口,是兩者的集大成者,相當於提供了“延遲”和“週期執行”功能的ThreadPoolExecutor。

Executors

靜態工廠類,提供了Executor、ExecutorService、ScheduledExecutorService、ThreadFactory 、Callable 等類的靜態工廠方法,通過這些工廠方法我們可以得到相對應的對象。

  1. 創建並返回設置有常用配置字符串的 ExecutorService 的方法。

  2. 創建並返回設置有常用配置字符串的ScheduledExecutorService 的方法。

  3. 創建並返回“包裝的”ExecutorService 方法,它通過使特定於實現的方法不可訪問來禁用重新配置。

  4. 創建並返回 ThreadFactory 的方法,它可將新創建的線程設置爲已知的狀態。

  5. 創建並返回非閉包形式的 Callable 的方法,這樣可將其用於需要 Callable 的執行方法中。

Executor框架是一個根據一組執行策略調用,調度,執行和控制的異步任務的框架。

無限制的創建線程會引起應用程序內存溢出。所以創建一個線程池是個更好的的解決方案,因爲可以限制線程的數量並且可以回收再利用這些線程。

利用Executors框架可以非常方便的創建一個線程池,

Java通過Executors提供四種線程池,分別爲:

newSingleThreadExecutor:創建一個線程的線程池,在這個線程池中始終只有一個線程存在。如果線程池中的線程因爲異常問題退出,那麼會有一個新的線程來替代它。此線程池保證所有任務的執行順序按照任務的提交順序執行。

newFixedThreadPool:創建固定大小的線程池。每次提交一個任務就創建一個線程,直到線程達到線程池的最大大小。線程池的大小一旦達到最大值就會保持不變,如果某個線程因爲執行異常而結束,那麼線程池會補充一個新線程。

newCachedThreadPool:可根據實際情況,調整線程數量的線程池,線程池中的線程數量不確定,如果有空閒線程會優先選擇空閒線程,如果沒有空閒線程並且此時有任務提交會創建新的線程。在正常開發中並不推薦這個線程池,因爲在極端情況下,會因爲 newCachedThreadPool 創建過多線程而耗盡 CPU 和內存資源。

newScheduledThreadPool:此線程池可以指定固定數量的線程來週期性的去執行。比如通過 scheduleAtFixedRate 或者 scheduleWithFixedDelay 來指定週期時間。

推薦使用ThreadPoolExecutor方式。

阿里的 Java 開發手冊時有一條是不推薦使用 Executors 去創建線程池,而是推薦去使用 ThreadPoolExecutor 來創建。

這樣做的目的主要原因是:使用 Executors 創建線程池不會傳入核心參數,而是採用的默認值,這樣的話我們往往會忽略掉裏面參數的含義,如果業務場景要求比較苛刻的話,存在資源耗盡的風險;另外採用 ThreadPoolExecutor 的方式可以讓我們更加清楚地瞭解線程池的運行規則,不管是面試還是對技術成長都有莫大的好處。

Future

Future接口和實現Future接口的FutureTask代表了線程池的異步計算結果。

AbstractExecutorService提供了newTaskFor()方法返回一個RunnableFuture,除此之外當我們把一個Runnable或者Callable提交給(submit())ThreadPoolExecutor或者ScheduledThreadPoolExecutor時,他們則會向我們返回一個FutureTask對象。如下:

 protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        return new FutureTask<T>(runnable, value);
    }
        protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        return new FutureTask<T>(callable);
    }
    <T> Future<T> submit(Callable<T> task)
    <T> Future<T> submit(Runnable task, T result)
    Future<> submit(Runnable task)

作爲異步計算的頂層接口,Future對具體的Runnable或者Callable任務提供了三種操作:執行任務的取消、查詢任務是否完成、獲取任務的執行結果。其接口定義如下:

public interface Future<V> {
    /**
     * 試圖取消對此任務的執行
     * 如果任務已完成、或已取消,或者由於某些其他原因而無法取消,則此嘗試將失敗。
     * 當調用 cancel 時,如果調用成功,而此任務尚未啓動,則此任務將永不運行。
     * 如果任務已經啓動,則 mayInterruptIfRunning 參數確定是否應該以試圖停止任務的方式來中斷執行此任務的線程
     */
    boolean cancel(boolean mayInterruptIfRunning);
    /**
     * 如果在任務正常完成前將其取消,則返回 true
     */
    boolean isCancelled();
    /**
     * 如果任務已完成,則返回 true
     */
    boolean isDone();
    /**
     *   如有必要,等待計算完成,然後獲取其結果
     */
    V get() throws InterruptedException, ExecutionException;
    /**
     * 如有必要,最多等待爲使計算完成所給定的時間之後,獲取其結果(如果結果可用)
     */
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

RunnableFuture

繼承Future、Runnable兩個接口,爲兩者的合體,即所謂的Runnable的Future。提供了一個run()方法可以完成Future並允許訪問其結果。

public interface RunnableFuture<V> extends Runnable, Future<V> {
    //在未被取消的情況下,將此 Future 設置爲計算的結果
    void run();
}

FutureTask

實現RunnableFuture接口,既可以作爲Runnable被執行,也可以作爲Future得到Callable的返回值。

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