事情起源于一個bug排查,一個AsyncTask的子類,執(zhí)行的時候發(fā)現(xiàn)onPreExecute方法執(zhí)行了,doInBackground卻遲遲沒有被調(diào)用。
懂AsyncTask一些表面原理的都知道,onPreExecute方法是在主線程執(zhí)行,doInBackground方法是在后臺線程執(zhí)行,所以很明顯是后臺線程被卡住了執(zhí)行不了,所以這就涉及到AsyncTask的原理問題了,查看出現(xiàn)bug的版本——Android 6.0源碼可以知道,AsyncTask里面維護(hù)著兩個線程池,THREAD_POOL_EXECUTOR和SERIAL_EXECUTOR,其中SERIAL_EXECUTOR是默認(rèn)的線程池:

如果用AsyncTask.execute(params...)方法來執(zhí)行任務(wù),就會用到默認(rèn)的線程池,即SERIAL_EXECUTOR;可以看出SERIAL_EXECUTOR會讓所有的線程串行執(zhí)行:
而且由于SERIAL_EXECUTOR被聲明為static,所以,同一個進(jìn)程里的AsyncTask都會共享這個線程池,這就意味著,在同一個進(jìn)程里,前面的線程不結(jié)束,后面的線程就會被掛起,這正是我遇到的情況。
接下來排查所有用AsyncTask.execute方法來執(zhí)行任務(wù)的情況,終于找到了一個不合理的調(diào)用————在doInBackground里請求網(wǎng)絡(luò),一直死等response,而沒有超時釋放。修復(fù)了這種情況,問題就迎刃而解了。
除了這種解決前面線程不合理設(shè)計的辦法,還有沒有別的解決方式呢,因?yàn)橛袝r候,我們的設(shè)計確實(shí)是讓后臺線程死循環(huán),不跳出的。
當(dāng)然有的,在AsyncTask設(shè)計上就考慮到了,前面說到AsyncTask里面還有一個線程池THREAD_POOL_EXECUTOR,從它的初始化參數(shù)可以看出,這是一個支持2到4個線程并行的線程池:
所以,使用AsyncTask執(zhí)行任務(wù)的時候,請使用AsyncTask.executeOnExecutor(THREAD_POOL_EXECUTOR)來讓你的任務(wù)跑在并行的線程池上,避免出現(xiàn)并前面線程阻塞的情況。當(dāng)然,如果你的CPU核心數(shù)夠多,2到4個線程的并行度不滿足的話,也可以自定義一個線程池來執(zhí)行AsyncTask,不過這樣的話,要注意自己維護(hù)這個線程池的初始化,釋放等等操作了。
PS:AsyncTask是不是一開始就是被設(shè)計成這樣的呢?筆者調(diào)研了一下,其實(shí)Android 1.5剛開始引入AsyncTask的時候,execute方法確實(shí)是串行執(zhí)行的,類定義里面只有SERIAL_EXECUTOR線程池;到1.6版本時,改用并行線程池THREAD_POOL_EXECUTOR,再到3.0版本至今,就成了上面說的模樣————定義兩個線程池,但是默認(rèn)用串行池。