串行還是并行?——記一次AsyncTask問題排查

事情起源于一個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個線程并行的線程池:

初始化參數(shù)
初始化參數(shù)

所以,使用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)用串行池。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容