串行仍是並行?——記一次 AsyncTask 問題排查

事情起源於一個bug排查,一個AsyncTask的子類,執行的時候發現onPreExecute方法執行了,doInBackground卻遲遲沒有被調用。
懂AsyncTask一些表面原理的都知道,onPreExecute方法是在主線程執行,doInBackground方法是在後臺線程執行,因此很明顯是後臺線程被卡住了執行不了,因此這就涉及到AsyncTask的原理問題了,查看出現bug的版本——Android 6.0源碼能夠知道,AsyncTask裏面維護着兩個線程池,THREAD_POOL_EXECUTOR和SERIAL_EXECUTOR,其中SERIAL_EXECUTOR是默認的線程池:網絡

線程池

若是用AsyncTask.execute(params...)方法來執行任務,就會用到默認的線程池,即SERIAL_EXECUTOR;能夠看出SERIAL_EXECUTOR會讓全部的線程串行執行:google

串行線程池

並且因爲SERIAL_EXECUTOR被聲明爲static,因此,同一個進程裏的AsyncTask都會共享這個線程池,這就意味着,在同一個進程裏,前面的線程不結束,後面的線程就會被掛起,這正是我遇到的狀況。
接下來排查全部用AsyncTask.execute方法來執行任務的狀況,終於找到了一個不合理的調用————在doInBackground裏請求網絡,一直死等response,而沒有超時釋放。修復了這種狀況,問題就迎刃而解了。線程

除了這種解決前面線程不合理設計的辦法,還有沒有別的解決方式呢,由於有時候,咱們的設計確實是讓後臺線程死循環,不跳出的。設計

固然有的,在AsyncTask設計上就考慮到了,前面說到AsyncTask裏面還有一個線程池THREAD_POOL_EXECUTOR,從它的初始化參數能夠看出,這是一個支持2到4個線程並行的線程池:cdn

初始化參數

因此,使用AsyncTask執行任務的時候,請使用AsyncTask.executeOnExecutor(THREAD_POOL_EXECUTOR)來讓你的任務跑在並行的線程池上,避免出現並前面線程阻塞的狀況。固然,若是你的CPU核心數夠多,2到4個線程的並行度不知足的話,也能夠自定義一個線程池來執行AsyncTask,不過這樣的話,要注意本身維護這個線程池的初始化,釋放等等操做了。blog

PS:AsyncTask是否是一開始就是被設計成這樣的呢?筆者調研了一下,其實Android 1.5剛開始引入AsyncTask的時候,execute方法確實是串行執行的,類定義裏面只有SERIAL_EXECUTOR線程池;到1.6版本時,改用並行線程池THREAD_POOL_EXECUTOR,再到3.0版本至今,就成了上面說的模樣————定義兩個線程池,可是默認用串行池。進程

相關文章
相關標籤/搜索