引言
在Android開(kāi)發(fā)中,只要是耗時(shí)的操作都需要開(kāi)啟一個(gè)線程來(lái)執(zhí)行。例如網(wǎng)絡(luò)訪問(wèn)必須放到子線程中執(zhí)行,否則會(huì)拋異常(NetworkOnMainThreadException),這樣做的目的也是為了防止用戶在主線程中做耗時(shí)操作,這樣很容易引起ANR。那么有了線程為什么還需要線程池呢?
線程的創(chuàng)建過(guò)程分為3步
- 創(chuàng)建線程 T1
- 執(zhí)行線程 T2
- 銷毀線程 T2
線程創(chuàng)建的總時(shí)間T=T1+T2+T3。 可以看出T1,T3是多線程本身的帶來(lái)的開(kāi)銷,我們渴望減少T1,T3所用的時(shí)間,從而減少T的時(shí)間。但一些線程的使用者并沒(méi)有注意到這一點(diǎn),所以在程序中頻繁的創(chuàng)建或銷毀線程,這導(dǎo)致T1和T3在T中占有相當(dāng)比例。顯然這是突出了線程的弱點(diǎn)(T1,T3),而不是優(yōu)點(diǎn)(并發(fā)性)。
合理利用線程池能夠帶來(lái)三個(gè)好處。
- 降低資源消耗。通過(guò)重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗。
- 提高響應(yīng)速度。當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不需要等到線程創(chuàng)建就能立即執(zhí)行。
- 提高線程的可管理性。線程是稀缺資源,如果無(wú)限制的創(chuàng)建,不僅會(huì)消耗系統(tǒng)資源,還會(huì)降低系統(tǒng)的穩(wěn)定性,使用線程池可以進(jìn)行統(tǒng)一的分配,調(diào)優(yōu)和監(jiān)控。但是要做到合理的利用線程池,必須對(duì)其原理了如指掌。
線程池的使用
首先我們來(lái)看下Java中線程池的使用。
Executor
Java中線程池基于Executor接口。Executor 接口中定義了一個(gè)方法 void execute(Runnable command)
,該方法接收一個(gè) Runable 實(shí)例,它用來(lái)執(zhí)行一個(gè)任務(wù),任務(wù)即一個(gè)實(shí)現(xiàn)了 Runnable 接口的類。
ExecutorService
ExecutorService 繼承自 Executor 接口,它提供了更豐富的實(shí)現(xiàn)多線程的方法。
-
shutdown()
方法來(lái)平滑地關(guān)閉 ExecutorService,調(diào)用該方法后,將導(dǎo)致 ExecutorService 停止接受任何新的任務(wù)且等待已經(jīng)提交的任務(wù)執(zhí)行完成(已經(jīng)提交的任務(wù)會(huì)分兩類:一類是已經(jīng)在執(zhí)行的,另一類是還沒(méi)有開(kāi)始執(zhí)行的),當(dāng)所有已經(jīng)提交的任務(wù)執(zhí)行完畢后將會(huì)關(guān)閉 ExecutorService。因此我們一般用該接口來(lái)實(shí)現(xiàn)和管理多線程。 -
submit(Runnable task)
方法與Executor的execute()
方法效果一樣,但是submit有返回值,如果我希望task執(zhí)行完后,每個(gè)task告訴我它的執(zhí)行結(jié)果,是成功還是失敗,如果是失敗,原因是什么,這時(shí)候就需要用到submit,通過(guò)返回的Future來(lái)獲取到結(jié)果
ThreadPoolExecutor
ThreadPoolExecutor 是正真線程池的實(shí)現(xiàn)類,我們創(chuàng)建線程的時(shí)候一般也是使用這個(gè)類。先看下構(gòu)造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
- corePoolSize 線程池的核心線程數(shù),也可以理解為最小線程數(shù),默認(rèn)情況下,核心線程在線程池中是一直存活的,即使當(dāng)前執(zhí)行的線程數(shù)量小于核心線程數(shù)量,所有的核心線程均會(huì)被創(chuàng)建出來(lái)。如果將ThreadPoolExecutor的allowCoreThreadTimeOut設(shè)置為ture,那么核心線程就會(huì)有超時(shí)策略。
- maximumPoolSize 線程池所能容納的最大線程數(shù),包括核心和非核心線程,當(dāng)活動(dòng)線程到達(dá)最大值,后續(xù)的任務(wù)就會(huì)被阻塞。
- keepAliveTime 非核心線程閑置時(shí)的超時(shí)時(shí)長(zhǎng),當(dāng)非核心線程空閑時(shí)間超過(guò)這個(gè)值,就會(huì)被終止。如果ThreadPoolExecutor的allowCoreThreadTimeOut設(shè)置為true,這個(gè)時(shí)間同樣會(huì)作用在核心線程上;
- unit 時(shí)間單位
- workQueue 線程池中的任務(wù)隊(duì)列,所有提交的Runable對(duì)象會(huì)存儲(chǔ)在這個(gè)參數(shù)中
- threadFactory 創(chuàng)建新線程時(shí)使用的factory
- handler 當(dāng)task出現(xiàn)異常時(shí),會(huì)通過(guò)這個(gè)handler通知外界
那么線程池針對(duì)不同的線程數(shù)量又是怎么執(zhí)行的呢,主要可以分為以下幾種情況
- 線程數(shù) < corePoolSize 所有核心線程都會(huì)用來(lái)處理任務(wù)。
- 線程數(shù) = corePoolSize 緩沖隊(duì)列workQueue未滿,任務(wù)被放入緩沖隊(duì)列。
- corePoolSize < 線程數(shù) < maximumPoolSize 緩沖隊(duì)列workQueue滿,創(chuàng)建新的線程來(lái)處理被添加的任務(wù)。
- corePoolSize < maximumPoolSize < 線程數(shù) 緩沖隊(duì)列workQueue滿,通過(guò) handler所指定的策略來(lái)處理此任務(wù)。
處理任務(wù)的優(yōu)先級(jí)為:核心線程corePoolSize、任務(wù)隊(duì)列workQueue、最大線程maximumPoolSize,如果三者都滿了,使用handler處理被拒絕的任務(wù)。 handler又有以下四種選擇
- CallerRunsPolicy 再次添加該task并執(zhí)行execute方法
- AbortPolicy 放棄該task并拋出RejectedExecutionException
- DiscardPolicy 放棄當(dāng)前task
- DiscardOldestPolicy 放棄等待時(shí)間最久的task
線程池的進(jìn)一步封裝
看了ThreadPoolExecutor的簡(jiǎn)單介紹是不是覺(jué)得使用過(guò)程比較復(fù)雜呢,沒(méi)關(guān)系,Java針對(duì)這一點(diǎn)還可以通過(guò)Executors的工廠方法配置,handler均使用的是defaultHandler即AbortPolicy只要線程數(shù)量大于核心線程數(shù)就會(huì)拋出異常需要自行處理。主要有以下五種:
- newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
核心線程數(shù)=最大線程數(shù),并且沒(méi)有超時(shí)策略。也就是說(shuō)該線程池沒(méi)有空閑狀態(tài),能最快速度響應(yīng)。
- newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
核心線程數(shù)=最大線程數(shù)=1,沒(méi)有超時(shí)策略。所有任務(wù)在一個(gè)線程中處理,不存在同步問(wèn)題。handler為AbortPolicy。
- newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
核心線程=0 ,最大線程=Integer.MAX_VALUE,這就意味著沒(méi)有線程數(shù)量的限制,超時(shí)時(shí)間為60毫秒。new SynchronousQueue<Runnable>
這個(gè)任務(wù)隊(duì)列比較特殊,簡(jiǎn)單理解為一個(gè)無(wú)法存儲(chǔ)的任務(wù)隊(duì)列。也就是說(shuō),當(dāng)有新的任務(wù)到來(lái),如果有空閑的線程,則將任務(wù)給空閑的線程處理,否則直接創(chuàng)建一個(gè)新的線程來(lái)處理任務(wù)。也就是說(shuō)一旦有任務(wù)來(lái)就會(huì)立刻執(zhí)行,但是消耗較大,所以比較適合用來(lái)處理大量的耗時(shí)較短的任務(wù)。
- newScheduledThreadPool
private static final long DEFAULT_KEEPALIVE_MILLIS = 10L;
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1));
}
核心線程為自定義 ,最大線程=Integer.MAX_VALUE,超時(shí)時(shí)間為10毫秒,也就是說(shuō)一旦線程空閑便立即回收。適用于定時(shí)任務(wù)與周期性任務(wù)。
- newWorkStealingPool (Java8中新引入)
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
這個(gè)線程池和其它四個(gè)不太一樣,是Java8中新引入的。線程數(shù)默認(rèn)為主機(jī)CPU的可用核心數(shù),且會(huì)動(dòng)態(tài)的增加與減少,提交的任務(wù)執(zhí)行順序并不能得到保證。
Android中的線程池
了解了Java提供的線程池我們就來(lái)看下Android中是怎樣使用線程池的。
AsyncTask是Android中異步處理的輕量級(jí)框架,其中的實(shí)現(xiàn)就是一個(gè)線程池。
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
在構(gòu)造方法中初始化了兩個(gè)變量mWorker與mFuture,這兩個(gè)變量很重要
- mWorker是一個(gè)WorkerRunnable
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
WorkerRunnable是一個(gè)實(shí)現(xiàn)了Callable接口的類。
public interface Callable<V> {
V call() throws Exception;
}
Callable接口里面只定義了一個(gè)call方法,返回一個(gè)泛型對(duì)象。那么Callable到底有什么用呢?線程無(wú)論繼承Thread類還是實(shí)現(xiàn)Runnable方法,執(zhí)行完任務(wù)以后都無(wú)法直接返回結(jié)果。而Callable接口就彌補(bǔ)了這個(gè)缺陷,當(dāng)call方法執(zhí)行完畢以后會(huì)返回一個(gè)泛型對(duì)象。WorkerRunnable實(shí)現(xiàn)了Callable接口,也就是說(shuō)我們可以調(diào)用mWorker.call()
來(lái)獲取返回的結(jié)果
- mFuture是一個(gè)FutureTask
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
參數(shù)是一個(gè)Callable,也就是mWorker。FutureTask實(shí)現(xiàn)了RunnableFuture接口。
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
這個(gè)接口又繼承了Runable與Future<V>。
Future<V>接口主要是針對(duì)Runnable和Callable任務(wù)的。
提供了三種功能:
- 判斷任務(wù)是否完成
boolean isDone()
- 能夠中斷任務(wù)
boolean cancel(boolean mayInterruptIfRunning);
- 能夠獲取任務(wù)執(zhí)行的結(jié)果
V get();
也就是說(shuō)FutureTask既能夠被線程執(zhí)行,又能提供線程執(zhí)行任務(wù)后返回的結(jié)果。
看完了構(gòu)造方法我們從execute()入手
public final AsyncTask<Params, Progress, Result> execute(Params... params)
{
return executeOnExecutor(sDefaultExecutor, params);
}
在執(zhí)行execute方法時(shí)傳入了初始化時(shí)的參數(shù)與sDefaultExecutor
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
sDefaultExecutor就是一個(gè)實(shí)現(xiàn)了Executor的基本類,execute中,在一個(gè)雙端隊(duì)列中不斷的插入Runable對(duì)象。ArrayDeque的內(nèi)部是使用數(shù)組形式來(lái)實(shí)現(xiàn)雙端隊(duì)列的,我們知道隊(duì)列是FIFO的,只能在隊(duì)頭刪除元素,隊(duì)尾添加元素,而雙端隊(duì)列是在隊(duì)頭和隊(duì)尾都能夠刪除和添加元素。需要注意的是ArrayDeque沒(méi)有容量的限制,隊(duì)列滿了以后會(huì)自動(dòng)進(jìn)行擴(kuò)充。
THREAD_POOL_EXECUTOR則是AsyncTask中的關(guān)鍵線程池
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
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;
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
AsyncTask在靜態(tài)代碼塊中初始化了一個(gè)ThreadPoolExecutor,核心線程數(shù)=[2,4/CPU核心數(shù)-1],最大線程數(shù)=CPU核心數(shù)*2+1,超時(shí)時(shí)間為30秒,隊(duì)列長(zhǎng)度為128,handler為AbortPolicy。并且設(shè)置了allowCoreThreadTimeOut(true),即核心線程超時(shí)可被回收。
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
在Asynck調(diào)用execute()時(shí),mWorker獲取到參數(shù),sDefaultExecutor開(kāi)始執(zhí)行execute方法。
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
回過(guò)來(lái)看,第一次執(zhí)行時(shí),mActive=null這時(shí)候便會(huì)執(zhí)行scheduleNext()
,當(dāng)隊(duì)列中有數(shù)據(jù)的時(shí)候便會(huì)把數(shù)據(jù)取出來(lái)放進(jìn)線程池中執(zhí)行。在執(zhí)行THREAD_POOL_EXECUTOR.execute(mActive)
時(shí),因?yàn)閙Future實(shí)現(xiàn)了Runable接口,這個(gè)時(shí)候便會(huì)調(diào)用run方法
public void run() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
其中的callable就是mWorker,此時(shí)會(huì)調(diào)用mWorker的call()
方法
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
};
這個(gè)就是在構(gòu)造方法中創(chuàng)建的,在call中實(shí)現(xiàn)了doInBackground方法,并把返回值給了postResult,postResult中的邏輯就是獲取一個(gè)Message,然后發(fā)送該Message給Handler處理。
簡(jiǎn)單回顧下,AsyncTask每次將doInBackground()
中的操作添加到線程池中執(zhí)行,執(zhí)行完后由handler傳遞數(shù)據(jù)。
總結(jié)
在Android開(kāi)發(fā)中對(duì)于線程池的使用可能僅僅只是一些異步的框架中有封裝,我們很少真正使用,僅僅new一個(gè)Thread,對(duì)于一些頻繁的請(qǐng)求與周期性的操作不如嘗試試用下線程池。
舉個(gè)例子:
我們需要每隔10秒進(jìn)行某個(gè)操作,這時(shí)你可能會(huì)考慮用一個(gè)定時(shí)器,每次開(kāi)一個(gè)線程去請(qǐng)求,這樣T1與T2的時(shí)間開(kāi)銷是很大的,此時(shí)使用線程池便能大大優(yōu)化性能。
如有錯(cuò)誤的地方歡迎大家留言指正探討。