Java-線程池總結

線程池的優勢:多線程

  • 重用線程,減小線程建立和銷燬的性能開銷。
  • 管理線程,並提供定時執行以及指定間隔循環執行等功能。

Android中的線程來源於Java中的Executor,實現類是ThreadPoolExecutor,ThreadPoolExecutor經過構造方法的一系列參數,來構成不一樣配置的線程池。經常使用的構造方法有下面四個:ide

ThreadPoolExecutor(int corePoolSize,
                        int maximumPoolSize,
                        long keepAliveTime,
                        TimeUnit unit,
                        BlockingQueue<Runnable> workQueue)
ThreadPoolExecutor(int corePoolSize,
                        int maximumPoolSize,
                        long keepAliveTime,
                        TimeUnit unit,
                        BlockingQueue<Runnable> workQueue,
                        ThreadFactory threadFactory)
ThreadPoolExecutor(int corePoolSize,
                        int maximumPoolSize,
                        long keepAliveTime,
                        TimeUnit unit,
                        BlockingQueue<Runnable> workQueue,
                        RejectedExecutionHandler handler)
ThreadPoolExecutor(int corePoolSize,
                        int maximumPoolSize,
                        long keepAliveTime,
                        TimeUnit unit,
                        BlockingQueue<Runnable> workQueue,
                        ThreadFactory threadFactory,
                        RejectedExecutionHandler handler)

參數說明

  • corePoolSize
    核心線程數,默認狀況核心線程會一直存活,即便處於閒置狀態也不會受keepAliveTime限制。除非將allowCoreThreadTimeOut設置爲true。
  • maximumPoolSize
    線程池所能容納的最大線程數。
  • keepAliveTime
    非核心線程的閒置超時時間,超過這個時間就會被回收。若是allowCoreThreadTimeOut設置爲true,核心線程也會被回收。
  • unit
    keepAliveTime的單位
  • workQueue
    線程池中的任務隊列。經常使用的有四種隊列:SynchronousQueue, LinkedBlockingDeque, ArrayBlockingQueue, DelayQueue。
  • threadFactory
    線程工廠,提供建立新線程的功能。經過線程工廠能夠對線程的一些屬性進行定製。
  • RejectedExecutionHandler
    當線程池中的資源已經所有使用,添加新線程被拒絕時,會調用RejectedExecutionHandler的rejectedExecution方法。

線程池的任務隊列(重要)

下面介紹上面說到的經常使用的四種隊列,在這以前先根據上面的參數分析一下幫助理解:有核心線程、非核心線程、任務隊列三個角色,他們能夠根據當前配置,對每一個新來的任務作出處理(處理就是要麼分配線程去執行任務,要麼把任務存到任務隊列裏等待分配線程)。那麼,三個角色能夠設計以下:
有限個核心線程 + 任務隊列(隊列大小可配置)+ 無限個非核心線程
(確切的說是corePoolSize個核心線程,maximumPoolSize - corePoolSize個非核心線程)工具

不難理解,任務不少的時候,核心線程不夠用了就存到任務隊列裏,隊列存滿了,就建立非核心線程執行任務(就像節假日客運站臨時加車同樣)。這樣設計包含了兩種極端狀況,就是隊列大小是0和隊列無限大,因此具體設計以下三種狀況:性能

  1. 有限個核心線程+無限個非核心線程,那麼來一個新任務就能給它分配一個線程去執行,這個時候任務隊列大小是0。
  2. 有限個核心線程+任務隊列(隊列無限大),那麼來一個新任務能夠給它分配線程去執行,無線程分配能夠存到隊列裏。
  3. 有限個核心線程+任務隊列(隊列大小有限且不是0)+無限個非核心線程。

這三種設計,就對應了workQueue參數的三種隊列:SynchronousQueue, LinkedBlockingDeque, ArrayBlockingQueue.net

SynchronousQueue

收到新任務時,直接交給線程處理,若是全部線程都在工做,那麼新建線程來處理這個任務。若是將maximumPoolSize指定成Integer.MAX_VALUE,就是無限個線程。
(對應上面的第1種設計)線程

LinkedBlockingDeque

這個隊列沒有大小限制。當接收到新任務時,若是當前線程小於核心線程數,則新建核心線程處理任務。若是當前線程數等於核心線程數,則進入隊列等待。因爲隊列沒有大小限制,也就致使了 maximumPoolSize 的設定失效,由於這時最大線程數不會超過核心線程數。
(對應上面第2種設計)設計

ArrayBlockingQueue

能夠限定隊列長度,收到任務的時候,若是沒有達到corePoolSize的值,則新建核心線程執行任務,若是達到了,入隊等候。若是隊列已滿,則新建非核心線程執行任務。若是達到了maximumPoolSize,就會發生錯誤。
(對應上面第3種設計)code

DelayQueue

隊列內元素必須實現 Delayed 接口,這就意味着你傳進去的任務必須先實現 Delayed 接口。這個隊列接收到任務時,首先先入隊,只有達到了指定的延時時間,纔會執行任務。blog

現實中,隊列不可能無限大,非核心線程不可能有無數個,那麼上面幾種隊列就有超出線程總數的狀況,這種狀況只須要配置ThreadPoolExecutor的第7個參數RejectedExecutionHandler便可(能夠翻到上面從新看下參數說明)。接口


4種常見的線程池

下面介紹4種系統提供的配置好的線程池,固然,若是理解了上面的任務隊列,本身配置出相同的線程池是很簡單的。這4種線程池使用系統的工具類Executors來建立,以下:

//下面加了參數1的是由於沒有無參數的重載方法。
Executors.newFixedThreadPool(1);
Executors.newCachedThreadPool();
Executors.newScheduledThreadPool(1);
Executors.newSingleThreadExecutor();
FixedThreadPool

對應:有限個核心線程+任務隊列(隊列無限大) --> LinkedBlockingQueue
它的建立方法須要傳入參數來指定核心線程數。功能特色很好理解,下面是藝術探索中對它的描述:

它是一種線程數量固定的線程池,當線程處於空閒狀態時,它們並不會被回收,除非線程池被關閉。當全部線程都處於活動狀態時,新任務就會處於等待狀態,直到有線程空閒出來。因爲FixedThreadPool只有核心線程而且這些核心線程不會被回收,這意味着它可以更加快速地響應外界請求。經過newFixedThreadPool方法的源碼能夠發現FixedThreadPool中只有核心線程沒有超時機制,另外任務隊列也是沒有大小限制的。

CachedThreadPool

對應:有限個核心線程+無限個非核心線程 --> SynchronousQueue
它的核心線程數是0,因此它的建立方法不須要參數,下面是藝術探索中對它的描述:

它是一種線程數量不定的線程池,它只有非核心線程,而且最大線程數爲Integer.MAX_VALUE。因爲Integer.MAX_VALUE是一個很大的數,實際上就至關於最大線程數能夠任意大。當線程池中的線程都處於活動狀態時,線程池會建立新的線程來處理新任務,不然就會利用空閒線程來處理新任務。線程池中的空閒線程都有超時機制,超時時長是60秒,超過60秒閒置的線程就會被回收。和FixedThreadPool不一樣的是,CachedThreadPool的任務隊列其實至關於一個空集合,這將致使任何任務都會被當即執行。這類線程池比較適合執行大量的耗時較少的任務。當整個線程池處於閒置狀態時,全部線程都會超時而被中止,這個時候CachedThreadPool中其實是沒有任何線程的,它幾乎是不佔任何系統資源的。

ScheduledThreadPool

對應:DelayQueue
它的建立方法須要參數來指定核心線程數,描述以下:

它的核心線程數是固定的,而非核心線程數是沒有限制的。這個線程池主要用於執行定時任務和有固定週期的重複任務。

SingleThreadExecutor

對應:有限個核心線程+任務隊列(隊列無限大) --> LinkedBlockingQueue
與FixedThreadPool的區別在於,SingleThreadExecutor只有一個核心線程,因此它的建立方法無需參數。描述以下:

這類線程池內部只有一個核心線程,它確保全部的任務都在同一個線程中按順序執行。意義在於統一全部外界任務到一個線程中,這使得在這些任務之間不須要處理線程同步的問題。



最後是使用線程池的幾個示例:

Runnable myRunnable = new Runnable() {
    @Override
    public void run() {
        SystemClock.sleep(2000);
    }
};

//本身配置的線程池
ThreadPoolExecutor myExecutor = new ThreadPoolExecutor(2, 10, 5, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
//執行myRunnable任務
myExecutor.execute(myRunnable);

//系統提供的線程池
ExecutorService fixedExecutor = Executors.newFixedThreadPool(1);
//執行myRunnable任務
fixedExecutor.execute(myRunnable);

//系統提供的線程池
ScheduledExecutorService sExecutor = Executors.newScheduledThreadPool(1);
//2000ms後執行myRunnable
sExecutor.schedule(myRunnable, 2000, TimeUnit.MILLISECONDS);
//10ms後,每隔1000ms執行一次myRunnable
sExecutor.scheduleAtFixedRate(myRunnable, 10, 1000, TimeUnit.MILLISECONDS);



文章參考

《Android開發藝術探索》
Java多線程-線程池ThreadPoolExecutor構造方法和規則
線程池,這一篇或許就夠了

相關文章
相關標籤/搜索