问题由来:
之前看到一篇博文,说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 }
@MainThread 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);// exec --> sDefaultExecutor return 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
时间: 2024-10-13 12:21:39