線程池Executor
目錄
1、線程池簡介
線程池是在java開發中很重要的一個概念,Android中的線程池和和java是保持一致的,並無什麼區別。線程池是一個抽象的概念,在java中是一個接口類,用Executor表示, 具體實現類爲ThreadPoolExecutor,它們位於java.util.concurrent包下面,這個接口類很短,而且接口聲明就只有一個,並且註釋中說明了一些情況怎麼使用,
package java.util.concurrent;
public interface Executor {
/**
* Executes the given command at some time in the future. The command
* may execute in a new thread, in a pooled thread, or in the calling
* thread, at the discretion of the {@code Executor} implementation.
*
* @param command the runnable task
* @throws RejectedExecutionException if this task cannot be
* accepted for execution
* @throws NullPointerException if command is null
*/
void execute(Runnable command);
}
上面省略了註釋,可以看註釋,很管用,上面有模版,教我們怎麼創建一個自己線程池,其中有段是這麼說的:許多線程池的實現類對將要執行的任務的方式和時間做了限制,下面展示了串型線程池在執行任務的時候,從一個任務到下一個任務的過程。
下面代碼來至註釋中的舉例,說明了一個從任務如何進入到下一個任務的串行執行過程:
package demo.xx.patten.xx;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.concurrent.Executor;
public class SerialExecutor implements Executor {
final Queue<Runnable> tasks = new ArrayDeque<>();
final Executor executor;
Runnable active;
public SerialExecutor(Executor executor) {
this.executor = executor;
}
public synchronized void execute(final Runnable r) {
tasks.add(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (active == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((active = tasks.poll()) != null) {
executor.execute(active);
}
}
}
上面的SerialExecutor管理了一個Runnable對象的任務列隊tasks和一個從構造函數中傳入的線程池,每次調用SerialExecutor的execute方法的時候都會傳入一個Runnable對象,然後將這個runnbale的執行方法run方法包裝進一個新建的Runnable對象中,然後將這個新建的Runnbale對象插入tasks隊列中。緊接着由於初始化的時候active是null,於是會首先執行scheduleNext()方法,這個時候通過tasks.poll()方法將插入的任務,也就是Runnable對象取出來交給構造函數中傳入的Executor對象實例來執行,注意上面try的finally塊中在r.run()被執行後都會被調用,這就保證了從一個任務到下一個任務的傳遞,因爲active在第一執行scheduleNext後會被負值,之後就不等於null了,那麼判斷語句就不會被執行了。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
}
- corePollSize: 線程池的核心線程數的最大數量,默認情況下,核心線程會在在線程中一直存活,即使處於閒置狀態。但如果將ThreadPoolExecutor的allowCoreThreadTimeOut屬性設置爲true,則核心線程數在閒置一段時間後,會被回收,這個事件由keepAliveTime指定;
- maxinumPoolSize: 線程池鎖容納的最大線程數,當活動線程達到這個數後,新來的任務將被阻塞;
- keepAliveTime: 非核心線程閒置的最大時長,超過後就會被回收,當allowCoreThreadTimeOut屬性設置爲true後,閒置的核心線程超過這個事件也會被回收;
- unit: 用於指定keepAliveTime參數的事件單位,這是一個枚舉類型,常用的有TimeUnit.MILLISECONDS, TimeUnit.SECONDS, TimeUnit.MINUTES等;
- workQueue: 線程池的任務隊列,通過線程池的execute方法提交的Runnable對象會存儲在這個隊列中;
- threadFactory: 線程工廠, 爲線程池提供創建新線程的功能,ThreadFactory只是一個接口,也只有一個方法: Thread newThread(Runnable r);
2、線程池的好處
- 線程複用,避免現線程創建和銷燬帶來的的性能開銷;
- 有效控制線程池的最大併發數,避免大量線程之間搶奪資源造成阻塞;
- 能夠對線程進行簡單的管理,並提供定時執行及指定間隔循環等功能;
3、線程池執行任務的規則
- 如果線程池中線程數量未達到核心線程的數量,會直接啓動一個核心線程去執行任務;
- 如果線程池中線程的數量已經達到或者超過了核心線程的數量,那麼接下來的任務會被插入到任務列隊(workQueue)中排隊等待執行;
- 如果workQueue中線程數量達到了列隊的上線,導致任務無法再插入任務列隊,並且線程池中的線程的數量沒有達到線程池設定的線程數上限,這種情況下會啓動非核心線程來執行任務;
- 如果步驟3中線程數量達到線程池規定最大值(maxmumPoolSize),那麼線程池會拒絕執行新添加的任務,並且ThreadPoolExecutor會調用RejectedExecutionHandler的的rejectedExecution方法來通知調用者。
4、線程池的分類
從前面的介紹我們瞭解到,要直接創建一個線程池,需要傳入很多個參數,這在實際使用中,就會顯得比較麻煩,所以在Executors這個線程池工具類中,給我們提供了更加簡單的創建線程池的API, 主要有四種類型,注意僅僅是類型,並不是存在的java類
1)FixedThreadPool類型
通過Executors.newFixedThreadPool(int nThreads)創建
創建一個固定線程數量的線程池,線程池中的所有線程都是核心線程,當線程空閒時不會被回收,也不會超時,除非線程池被關閉,當線程池中線程都處於活動狀態的時候,新進任務會處於等待狀態,直到有線程空出來去處理,處於等待狀態的任務會在任務列隊中排隊等候,這個列隊的容量沒有大小限制,由於線程不會被回收,這樣便能夠快速的響應外界請求。
2)CachedThreadPool類型
通過Executors.newCachedThreadPool()創建
這是一個線程池的線程的數量是不定的,所有線程都是非核心線程,最大線程數量爲Integer.MAX_VALUE,當線程池中線程都處於活動狀態的時候,線程池會創建新的線程池來處理新進任務,否則就會利用空閒線程來處理新進任務,線程池中的線程都有60s的超時機制,超過時間的線程會被回收。
使用場景:大量的耗時較少的任務
3)ScheduleThreadPool類型
通過Executors.newScheduleThreadPool(int corePoolSize)創建。
這種線程池的核心線程數是固定的,非核心線程數量沒有限制,並且非核心線程執行完任務會被立刻回收,非核心線程沒有閒置的狀態。
使用場景:定時任務和固定週期的重複任務
4)SingleThreadExecutor類型
通過Executors.newSingleThreadExecutor()創建
SingleThreadExecutor內部只有唯一一個核心線程用來執行任務,其確保了所有的任務都在一個線程中執行,SingleThreadExecutor存在的意義在於統一所有的外界任務到一個線程中去執行,這樣這些任務就不需要處理線程同步的問題。