[TOC]
因為建立和銷燬執行緒是需要代價的,所以當任務需要頻繁開啟執行緒的時候就需要使用執行緒池去管理。
1.執行緒池的建立
比較常見的是
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory
複製程式碼
這個THREAD_POOL_EXECUTOR
就是一個執行緒池,執行緒池都實現了ExecutorService的介面,定義了execute,shutdown,submit等方法。
下面解釋一下引數的含義。
-
corePoolSize
corePoolSize
表示核心執行緒數,corePoolSize
數量的執行緒會一直存活,除非設定了allowCoreThreadTimeOut
。 -
maximumPoolSize
maximumPoolSize
是最大執行緒數量,maximumPoolSize
一定是大於corePoolSize
的,否則會報錯。噹噹前執行緒數量大於corePoolSize
小於maximumPoolSize
,並且阻塞佇列滿了,則建立新執行緒。 -
keepAliveTime
keepAliveTime
代表最大執行緒數量內,核心執行緒外的執行緒在空閒多久內不會被銷燬,也就是超過這個時間就會銷燬。 -
unit
TimeUnit.SECONDS
代表keepAliveTime
的時間單位。 -
workQueue 在任務執行前儲存任務的佇列,噹噹前執行緒池執行緒數量達到
corePoolSize
並且當前所有執行緒都處於活動狀態,則execute提交的runnable儲存到佇列中。 -
threadFactory 當executor建立新執行緒的時候的工廠方法 例如:
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
複製程式碼
還有一種:
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExcutionHandler handler
複製程式碼
這個handler
是workQueue
滿了之後對新加任務的處理策略,RejectedExcutionHandler是一個介面,包含
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
方法,後面會講到。
2.封裝的執行緒池
不過,Java已經為我們封裝了一些執行緒池,在Executors
中包含很多靜態方法。
- 1.newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
複製程式碼
這個方法建立了一個數量固定的執行緒池,並且工作佇列是一個無限制大小的LinkedBlockingQueue。最大執行緒數量和核心執行緒數量相同。
- 2.newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService(
new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
}
複製程式碼
newSingleThreadExecutor
建立一個單執行緒,無限制的佇列。所以佇列的任務都是以順序的方式執行。
- 3.newCachedThreadPool
適合執行大量短時間非同步任務的執行緒池。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
}
複製程式碼
建立一個核心執行緒數量0,最大執行緒數量為Integer.MAX_VALUE的執行緒池,執行緒存活時間60s,任務佇列為SynchronousQueue。
關於SynchronousQueue
SynchronousQueue是一個阻塞佇列,內部沒有容量,也就是capacity是0,每一個insert操作必須等另一個執行緒的remove操作。對SynchronousQueue使用peek操作都會返回null,也不能使用迭代器iterate。
- 4.newScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
複製程式碼
最終都會呼叫到
public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS, new DelayedWorkQueue(), threadFactory);}
複製程式碼
可以看到ScheduledThreadPoolExecutor是一個有固定數量的核心執行緒,最大執行緒數量是Integer.MAX_VALUE,如果執行緒執行完畢則會立即銷燬。任務佇列是DelayedWorkQueue。
通常ScheduledThreadPoolExecutor
會定時執行任務,例如:
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
複製程式碼
initialDelay
是第一次執行延長的時間,period
是執行週期的次數。
DelayedWorkQueue
DelayedWorkQueue是ScheduledThreadPoolExecutor的內部類。 Java執行緒學習筆記(十一) DelayQueue的應用
3.任務佇列
- ArrayBlockingQueue
基於陣列結構的有界佇列,按FIFO規則排序。佇列滿的話,還有任務想進入佇列,則呼叫拒絕策略。
- LinkedBlockingQueue
基於連結串列的無界佇列,按FIFO排序,由於是無界的,所以採用此佇列後執行緒池將忽略拒絕策略引數,還會忽略最大執行緒數的引數。
- SynchronousQueue
SynchronousQueue
內部沒有快取,每一個 insert
操作必須對應於另外一個執行緒的remove
操作,反之亦然。你不能插入一個元素,除非有另外一個執行緒嘗試著remove。
其實SynchronousQueue的思想就是傳遞的性質,有人取得時候我才能加,僅此而已。所以SynchronousQueue 執行put()
的時候不會返回,除非有對應的take()
, 但是像大小是1的LinkedBlockingQueue,put() 操作會立即返回。SynchronousQueue內部使用了大量的ReentrantLock。
在stackoverflow上的回答有助於理解。
- PriorityBlockingQueue
具有優先順序佇列的有界佇列,可自定義優先順序。
4.拒絕策略RejectedExcutionHandler
當執行緒池的任務快取佇列已滿並且執行緒池中的執行緒數目達到maximumPoolSize,如果還有任務到來就會採取任務拒絕策略。
AbortPolicy
、CallerRunsPolicy
、DiscardOldestPolicy
、DiscardPolicy
都是在ThreadPoolExecutor中宣告的。
- AbortPolicy
拒絕任務,丟擲RejectedExecutionException異常。
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString());}
複製程式碼
- CallerRunsPolicy
拒絕新任務進入,如果執行緒池沒有關閉,那麼新任務執行在呼叫executor
的執行緒中直接執行。
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
複製程式碼
- DiscardOldestPolicy
當執行緒池沒有關閉時,拋棄最老的還沒有執行的請求,然後執行
execute
。
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
複製程式碼
- DiscardPolicy
拋棄不能加入的任務,除此之外不做任何處理
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {}
複製程式碼
舉例
ThreadPoolExecutor pool = xxxxxx;
pool.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
複製程式碼
AsyncTask原始碼中的設定
在AsyncTask中,核心執行緒數量是CPU數量+1.
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
複製程式碼
最大執行緒數量是設定為2*CPU數量+1:
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
複製程式碼
sPoolWorkQueue
是任務的儲存佇列,在這裡是用一個大小128的連結串列去儲存的:
private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(128);
複製程式碼