問題由來:
? ? ?之前看到一篇博文,說AsyncTask不適合運行多任務, 多個任務不會異步執行, 當時只是印象里記住了一下也不確定, 今天把代碼看了看, 把原因寫出來。
?
問題的代碼演示:
?
1 public class AsyncTaskDemo extends AsyncTask<String, Integer, String>{ 2 private final static String TAG = "AsyncTaskTest"; 3 4 @Override 5 protected String doInBackground(String... params) { 6 Log.v(TAG, params[0] + "=================doInBackground===================PID = " + Thread.currentThread().getId()); 7 8 return params[0]; 9 } 10 11 12 }
?
1 // 一個按鈕的onclick函數 2 public void test(View view) { 3 Log.v("AsyncTaskTest", "CPU_COUNT = " + Runtime.getRuntime().availableProcessors()); 4 5 new AsyncTaskDemo().execute("Hello1"); 6 new AsyncTaskDemo().execute("Hello2"); 7 new AsyncTaskDemo().execute("Hello3"); 8 new AsyncTaskDemo().execute("Hello4"); 9 }
//logcat里打印出來的結果12-11 16:07:34.865 1979-1979/com.sabo.helloworld V/AsyncTaskTest﹕ CPU_COUNT = 1 12-11 16:07:34.871 1979-2102/com.sabo.helloworld V/AsyncTaskTest﹕ Hello1=================doInBackground===================PID = 161 12-11 16:07:34.872 1979-2103/com.sabo.helloworld V/AsyncTaskTest﹕ Hello2=================doInBackground===================PID = 162 12-11 16:07:34.872 1979-2103/com.sabo.helloworld V/AsyncTaskTest﹕ Hello3=================doInBackground===================PID = 162 12-11 16:07:34.872 1979-2103/com.sabo.helloworld V/AsyncTaskTest﹕ Hello4=================doInBackground===================PID = 162
實驗大致輔證了"AsyncTask不適合運行多任務"這個推斷, 另一個有趣的結果是非異步執行的時候居然可能不是在一個線程里運行的【看我上面用紅色標記的部分】。
?
分析:
?
1 @MainThread 2 public final AsyncTask<Params, Progress, Result> execute(Params... params) { 3 return executeOnExecutor(sDefaultExecutor, params); 4 }
?
@MainThreadpublic 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);// exec --> sDefaultExecutorreturn this;}
如果熟悉Java的并發編程的話就知道sDefaultExecutor用于將要完成的任務交給內部已經實現的線程池去執行(有興趣的話可以去看看Doug Lea寫的那本《Java并發編程》)
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);}}}
這里的代碼就能解釋為什么多個AsyncTask一起執行時序列化執行而被異步的了,?
1 new AsyncTaskDemo().execute("Hello1"); //mActive == null 2 new AsyncTaskDemo().execute("Hello2"); //mActive != null 3 new AsyncTaskDemo().execute("Hello3"); //mActive != null 4 new AsyncTaskDemo().execute("Hello4"); //mActive != null
上面紅色標記的代碼可以看出,只有第一個任務直接調用scheduleNext()--->THREAD_POOL_EXECUTOR.execute(mActive), 線程池直接將任務交由線程去執行,而后面幾個任務先被放入ArrayDeque<Runnable> mTasks, 沒有交給任何線程去執行,
每個任務執行完之后又都要運行上面綠顏色標記的scheduleNext(), 從而依次序序列化執行任務。
?
上面提到的有趣的事情,既然AsyncTask是序列化執行任務的, 那么線程池里只要一個線程就能滿足要求了啊, 為什么會有兩個線程。
1 private static final int CORE_POOL_SIZE = CPU_COUNT + 1; 2 private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; 3 private static final int KEEP_ALIVE = 1; 4 5 public static final Executor THREAD_POOL_EXECUTOR 6 = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, 7 TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
上面我把CPU_COUNT的值打印出來是1, 所以這里線程池的線程數就可能是[2, 3]了, 然而對于AsyncTask來說1就夠了, 多余1的線程其實是沒有什么幫助的。
?
P.S.
:-)第一次寫博客, 加上功力又有限, 歡迎大家指正canbin.zhang#qq.com
? ? ?