線程池ThreadPoolExecutor
JDK5帶來的一大改進就是Java的并發能力,它提供了三種并發武器:并發框架Executor,并發集合類型如ConcurrentHashMap,并發控制類如CountDownLatch等;圣經《Effective Java》也說,盡量使用Exector而不是直接用Thread類進行并發編程。
AsyncTask內部也使用了線程池處理并發;線程池通過ThreadPoolExector
類構造,這個構造函數參數比較多,它允許開發者對線程池進行定制,我們先看看這每個參數是什么意思,然后看看Android是以何種方式定制的。
ThreadPoolExecutor的其他構造函數最終都會調用如下的構造函數完成對象創建工作:
1234567
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
corePoolSize: 核心線程數目,即使線程池沒有任務,核心線程也不會終止(除非設置了allowCoreThreadTimeOut參數)可以理解為“常駐線程”
maximumPoolSize: 線程池中允許的最大線程數目;一般來說,線程越多,線程調度開銷越大;因此一般都有這個限制。
keepAliveTime: 當線程池中的線程數目比核心線程多的時候,如果超過這個keepAliveTime的時間,多余的線程會被回收;這些與核心線程相對的線程通常被稱為緩存線程
unit: keepAliveTime的時間單位
workQueue: 任務執行前保存任務的隊列;這個隊列僅保存由execute提交的Runnable任務
threadFactory: 用來構造線程池的工廠;一般都是使用默認的;
handler: 當線程池由于線程數目和隊列限制而導致后續任務阻塞的時候,線程池的處理方式。
那么,當一個新的任務到達的時候,線程池中的線程是如何調度的呢?(別慌,講這么一大段線程池的知識,是為了理解AsyncTask;Be Patient)
如果線程池中線程的數目少于corePoolSize,就算線程池中有其他的沒事做的核心線程,線程池還是會重新創建一個核心線程;直到核心線程數目到達corePoolSize(常駐線程就位)
如果線程池中線程的數目大于或者等于corePoolSize,但是工作隊列workQueue沒有滿,那么新的任務會放在隊列workQueue中,按照FIFO的原則依次等待執行;(當有核心線程處理完任務空閑出來后,會檢查這個工作隊列然后取出任務默默執行去)
如果線程池中線程數目大于等于corePoolSize,并且工作隊列workQueue滿了,但是總線程數目小于maximumPoolSize,那么直接創建一個線程處理被添加的任務。
如果工作隊列滿了,并且線程池中線程的數目到達了最大數目maximumPoolSize,那么就會用最后一個構造參數handler
處理;**默認的處理方式是直接丟掉任務,然后拋出一個異常。
總結起來,也即是說,當有新的任務要處理時,先看線程池中的線程數量是否大于 corePoolSize,再看緩沖隊列 workQueue 是否滿,最后看線程池中的線程數量是否大于 maximumPoolSize。另外,當線程池中的線程數量大于 corePoolSize 時,如果里面有線程的空閑時間超過了 keepAliveTime,就將其移除線程池,這樣,可以動態地調整線程池中線程的數量。
我們以API 22為例,看一看AsyncTask里面的線程池是以什么參數構造的;AsyncTask里面有“兩個”線程池;一個THREAD_POOL_EXECUTOR
一個SERIAL_EXECUTOR
;之所以打引號,是因為其實SERIAL_EXECUTOR
也使用THREAD_POOL_EXECUTOR
實現的,只不過加了一個隊列弄成了串行而已,那么這個THREAD_POOL_EXECUTOR
是如何構造的呢?
123456789
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;private static final int KEEP_ALIVE = 1;private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(128); public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
可以看到,AsyncTask里面線程池是一個核心線程數為CPU + 1
,最大線程數為CPU * 2 + 1
,工作隊列長度為128的線程池;并且沒有傳遞handler
參數,那么使用的就是默認的Handler(拒絕執行).
那么問題來了:
如果任務過多,那么超過了工作隊列以及線程數目的限制導致這個線程池發生阻塞,那么悲劇發生,默認的處理方式會直接拋出一個異常導致進程掛掉。假設你自己寫一個異步圖片加載的框架,然后用AsyncTask實現的話,當你快速滑動ListView的時候很容易發生這種異常;這也是為什么各大ImageLoader都是自己寫線程池和Handlder的原因。
這個線程池是一個靜態變量;那么在同一個進程之內,所有地方使用到的AsyncTask默認構造函數構造出來的AsyncTask都使用的是同一個線程池,如果App模塊比較多并且不加控制的話,很容易滿足第一條的崩潰條件;如果你不幸在不同的AsyncTask的doInBackgroud里面訪問了共享資源,那么就會發生各種并發編程問題。
在AsyncTask全部執行完畢之后,進程中還是會常駐corePoolSize個線程;在Android 4.4 (API 19)以下,這個corePoolSize是hardcode的,數值是5;API 19改成了cpu + 1
;也就是說,在Android 4.4以前;如果你執行了超過五個AsyncTask;然后啥也不干了,進程中還是會有5個AsyncTask線程;不信,你看:
Handler
AsyncTask里面的handler很簡單,如下(API 22代碼):
12345
private static final InternalHandler sHandler = new InternalHandler();public InternalHandler() { super(Looper.getMainLooper());}
注意,這里直接用的主線程的Looper;如果去看API 22以下的代碼,會發現它沒有這個構造函數,而是使用默認的;默認情況下,Handler會使用當前線程的Looper,如果你的AsyncTask是在子線程創建的,那么很不幸,你的onPreExecute
和onPostExecute
并非在UI線程執行,而是被Handler post到創建它的那個線程執行;如果你在這兩個線程更新了UI,那么直接導致崩潰。這也是大家口口相傳的AsyncTask必須在主線程創建的原因。
另外,AsyncTask里面的這個Handler是一個靜態變量,也就是說它是在類加載的時候創建的;如果在你的APP進程里面,以前從來沒有使用過AsyncTask,然后在子線程使用AsyncTask的相關變量,那么導致靜態Handler初始化,如果在API 16以下,那么會出現上面同樣的問題;這就是AsyncTask必須在主線程初始化 的原因。
事實上,在Android 4.1(API 16)以后,在APP主線程ActivityThread的main函數里面,直接調用了AscynTask.init
函數確保這個類是在主線程初始化的;另外,init這個函數里面獲取了InternalHandler
的Looper,由于是在主線程執行的,因此,AsyncTask的Handler用的也是主線程的Looper。這個問題從而得到徹底的解決。
AsyncTask是并行執行的嗎?
現在知道AsyncTask內部有一個線程池,那么派發給AsyncTask的任務是并行執行的嗎?
答案是不確定。在Android 1.5剛引入的時候,AsyncTask的execute
是串行執行的;到了Android 1.6直到Android 2.3.2,又被修改為并行執行了,這個執行任務的線程池就是THREAD_POOL_EXECUTOR
,因此在一個進程內,所有的AsyncTask都是并行執行的;但是在Android 3.0以后,如果你使用execute
函數直接執行AsyncTask,那么這些任務是串行執行的;(你說蛋疼不)源代碼如下:
123
public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params);}
這個sDefaultExecutor
就是用來執行任務的線程池,那么它的值是什么呢?繼續看代碼:
1
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
因此結論就來了:Android 3.0以上,AsyncTask默認并不是并行執行的;
為什么默認不并行執行?
也許你不理解,為什么AsyncTask默認把它設計為串行執行的呢?
由于一個進程內所有的AsyncTask都是使用的同一個線程池執行任務;如果同時有幾個AsyncTask一起并行執行的話,恰好AysncTask的使用者在doInbackgroud
里面訪問了相同的資源,但是自己沒有處理同步問題;那么就有可能導致災難性的后果!
由于開發者通常不會意識到需要對他們創建的所有的AsyncTask對象里面的doInbackgroud
做同步處理,因此,API的設計者為了避免這種無意中訪問并發資源的問題,干脆把這個API設置為默認所有串行執行的了。如果你明確知道自己需要并行處理任務,那么你需要使用executeOnExecutor(Executor exec,Params... params)
這個函數來指定你用來執行任務的線程池,同時為自己的行為負責。(處理同步問題)
實際上《Effective Java》里面有一條原則說的就是這種情況:不要在同步塊里面調用不可信的外來函數。這里明顯違背了這個原則:AsyncTask這個類并不知道使用者會在doInBackgroud
這個函數里面做什么,但是對它的行為做了某種假設。
如何讓AsyncTask并行執行?
正如上面所說,如果你確定自己做好了同步處理,或者你沒有在不同的AsyncTask里面訪問共享資源,需要AsyncTask能夠并行處理任務的話,你可以用帶有兩個參數的executeOnExecutor
執行任務:
1234567
new AsyncTask<Void, Void, Vo @Override protected Void doInBackground(Void... params) { // do something return null; }}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
更好的AsyncTask
從上面的分析得知,AsyncTask有如下問題:
默認的AsyncTask如果處理的任務過多,會導致程序直接崩潰;
AsyncTask類必須在主線程初始化,必須在主線程創建,不然在API 16以下很大概率崩潰。
如果你曾經使用過AsyncTask,以后不用了;在Android 4.4以下,進程內也默認有5個AsyncTask線程;在Android 4.4以上,默認有CPU + 1
個線程。
Android 3.0以上的AsyncTask默認是串行執行任務的;如果要并行執行需要調用低版本沒有的API,處理麻煩。
因此我們對系統的AsyncTask做了一些修改,在不同Android版本提供一致的行為,并且提高了使用此類的安全性,主要改動如下:
添加對于任務過多導致崩潰的異常保護;在這里進行必要的數據統計上報工作;如果出現這個問題,說明AsyncTask不適合這種場景了,需要考慮重構;
移植API 22對于Handler的處理;這樣就算在線程創建異步任務,也不會有任何問題;
提供串行執行和并行執行的execute
方法;默認串行執行,如果明確知道自己在干什么,可以使用executeParallel
并行執行。
在doInbackgroud
里面頻繁崩潰的地方加上try..catch
;自己處理數據上報工作。
完整代碼見gist,BetterAsyncTask
原文地址:http://weishu.me/2016/01/18/dive-into-asynctask/
大體流程
? excute()方法中首先直接調用preExcute()方法
? AsyncTask的構造方法構造了mWorkder這個WorkerRunnable對象.再用mWorker構造一個FutureTask對象丟到線程池里面去執行
? AsyncTask的成員變量有個InternalHandler,構造的時候進行初始化.
? FutureTask轉調mWorker的run方法最后在線程池的子線程調用doInBackGround()方法.
? 然后FutureTask調用setResult()方法把運行結果返回,完成后調用FutureTask的done方法.這里面用InternalHandler發了個消息給主線程,最后拿到結果調用 finish()
- FutureTask類
它需要Callable接口類型的參數,在AsyncTask類中,創建了WorkerRunnable的實現類和FutureTask類,在run方法中調用
private static abstract class WorkerRunnable<Params, Result>
implements Callable<Result> {
Params[] mParams;
}
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
//調用熟悉的doInBackground方法,在子線程中執行,
return postResult(doInBackground(mParams));
}
};
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 occured
while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
//執行execute方法時
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
onPreExecute();
mWorker.mParams = params;
//將任務添加到Executor的實現類ThreadPoolExecutor里面
exec.execute(mFuture);
}
//FutureTask任務
public class FutureTask<V> implements RunnableFuture<V>{
private volatile int state;//任務運行的狀態
//構造函數java.util.concurrent.Callable
public FutureTask(Callable<V> callable) {
}
//取消任務通過線程執行interrupt
public boolean cancel(boolean mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally { // final state
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
}
}
//此方法調用awaitDone,它會阻塞線程,一直等到線程執行完成
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
//變量c就是構造函數傳的Callable<V> callable對象
public void run() {
if (c != null && state == NEW) {
V result;
result = c.call();
//call里面的任務執行完畢之后,在set方法里面調用finishCompletion方法,
之后調用done方法表示任務執行完成。
set(result);
}
}
//消除和信號,所有等待的線程,調用done()
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
for (;;) {
Thread t = q.thread;
if (t != null) {
q.thread = null;
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
}
done();
callable = null; // to reduce footprint
}
//異步任務執行完成,回調
protected void done() {
}
}
ThreadPoolExecutor
任務執行在這個類里面用到了ReentrantLock鎖,實現了ExecutorService接口。
//addWorker方法第二個參數core含義:true表示核心線程5,false表示非核心線程128
public void execute(Runnable command) {
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
//調用handler.rejectedExecution拋出異常
reject(command);
}
private boolean addWorker(Runnable firstTask, boolean core) {
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
for (;;) {
int wc = workerCountOf(c);//當前work線程數量
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
}
}
Worker w = new Worker(firstTask);
Thread t = w.thread;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
workers.add(w);
} finally {
mainLock.unlock();
}
t.start();
return true;
}
InternalHandler
任務執行完時在子線程,此時會發送一個handler消息,handler接收到這個消息就會調用AsyncTask的finish方法,接著調用onPostExecute。
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
private static class InternalHandler extends Handler {
public void handleMessage(Message msg) {
AsyncTaskResult result = (AsyncTaskResult) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
異步任務執行的結果,主線程無法輕易的獲取
Java FutureTask 異步任務操作提供了便利性
1.獲取異步任務的返回值
2.監聽異步任務的執行完畢
3.取消異步任務
doBackground(call)
call的返回值在Future的done方法中獲取
->onPostExecute
new MyTask().execute();
實例化:
new AsyncTask() -> new FutureTask()
執行:
Executor.execute(mFuture) -> SerialExecutor.myTasks(隊列)
-> (線程池)THREAD_POOL_EXECUTOR.execute
線程池中的所有線程,為了執行異步任務
CORE_POOL_SIZE 核心線程數
MAXIMUM_POOL_SIZE 最大線程數量
KEEP_ALIVE 1s閑置回收
TimeUnit.SECONDS 時間單位
sPoolWorkQueue 異步任務隊列
sThreadFactory 線程工廠
如果當前線程池中的數量小于corePoolSize,創建并添加的任務。
如果當前線程池中的數量等于corePoolSize,緩沖隊列 workQueue未滿,那么任務被放入緩沖隊列、等待任務調度執行。
如果當前線程池中的數量大于corePoolSize,緩沖隊列workQueue已滿,并且線程池中的數量小于maximumPoolSize,新提交任務會創建新線程執行任務。
如果當前線程池中的數量大于corePoolSize,緩沖隊列workQueue已滿,并且線程池中的數量等于maximumPoolSize,新提交任務由Handler處理。
當線程池中的線程大于corePoolSize時,多余線程空閑時間超過keepAliveTime時,會關閉這部分線程。
public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
1.線程池容量不夠拋出異常
2.內存泄露
3.一個線程,一個異步任務(?)
測試代碼
public class FutureTest1 {
public static void main(String[] args) {
System.out.println("main ID: " + Thread.currentThread().getId());
Task work = new Task();
FutureTask<Integer> future = new FutureTask<Integer>(work) {
// 異步任務執行完成,回調
@Override
protected void done() {
try {
System.out.println("done:" + get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
};
// 線程池(使用了預定義的配置)
ExecutorService executor = Executors.newCachedThreadPool();
executor.execute(future);
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
// 取消異步任務
// future.cancel(true);
try {
// 阻塞,等待異步任務執行完畢
System.out.println(future.get()); // 獲取異步任務的返回值
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
// 異步任務
static class Task implements Callable<Integer> {
// 返回異步任務的執行結果
@Override
public Integer call() throws Exception {
int i = 0;
for (; i < 10; i++) {
try {
System.out.println(Thread.currentThread().getName() + "_"
+ i);
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("ID: " + Thread.currentThread().getId());
return i;
}
}
}