Android 的執行緒和執行緒池
在作業系統中,執行緒是作業系統排程的最小單元,同時執行緒又是一種受限的系統資源,即執行緒不可能無限制地產生,並且執行緒的建立和銷燬都會有相應的開銷。在 Android 中除了 Thread 以外,還有 AsyncTask、IntentService 以及 HandlerThread 都扮演著執行緒的角色。
主執行緒和子執行緒
在 Java 中預設情況下一個程序只有一個執行緒,這個就是主執行緒,除主執行緒以外的執行緒都叫子執行緒,Android 沿用了同樣的執行緒模型。
Android 中的執行緒形態
AsyncTask
- AsyncTask 是一個輕量級的非同步任務類,線上程池中執行後臺任務,然後把進度和最終結果傳遞到主執行緒更新 UI。
-
AsyncTask 是一個抽象的泛型類,提供了
Params
、Progress
和Result
三個泛型引數。如果不需要傳遞具體的引數,引數型別可以使用Void
來代替。public abstract class AsyncTask <Params, Progress, Result>
-
AsyncTask 提供 4 個核心方法:
- onPreExecute() :在主執行緒中執行,一般用於做一些準備工作。
- doInBackground(Params… params) :線上程池中執行非同步任務,params 為輸入引數。在此方法中可以呼叫
publishProgress(Progress)
方法來更新任務進度。最終需要返回結果給onPostExecute
方法。 - onProgressUpdate(Progess… values) :在主執行緒中執行,任務進度更新時會呼叫此方法。
- onPostExecute(Result result) :在主執行緒中執行,result 為非同步任務中返回的結果。
- AsyncTask 使用時的注意點:
- AsyncTask 的類必須在主執行緒中載入,這一過程在 Android 4.1 及以上版本被系統自動完成(ActivityThread 的
main
方法會呼叫 AsyncTask 的init
方法)。 - AsyncTask 的物件必須在主執行緒中建立。
-
execute
方法必須在 UI 執行緒中呼叫。 - 不要在程式中直接呼叫
onPreExecute
、onPostExecute
、doInBackground
和onProgressUpdate
方法。 - 一個 AsyncTask 物件只能執行一次,即只能呼叫一次
execute
方法,否則會報錯。 - 在 Android 1.6 之前,AsyncTask 是序列執行任務的,Android 1.6 的時候開始採用執行緒池處理並行任務,但是從 Android 3.0 開始,又換回了序列執行任務的方式,不過我們可以通過
executeOnExecutor
方法執行並行任務。
- AsyncTask 的類必須在主執行緒中載入,這一過程在 Android 4.1 及以上版本被系統自動完成(ActivityThread 的
AsyncTask 的工作原理
AsyncTask 中有兩個執行緒池(SerialExecutor 和 THREAD_POOL_EXECUTOR)和一個 Handler(InternalHandler)。其中執行緒池 SerialExecutor 用於任務的排隊,而執行緒池 THREAD_POOL_EXECUTOR 用於真正地執行任務,InternalHandler 用於將執行環境從執行緒池切換到主執行緒。
HandlerThread
- HandlerThread 繼承了 Thread,它是一種可以使用 Handler 的 Thread。
- HandlerThread 的實現就是在其
run
方法中通過Looper.prepare()
來建立訊息佇列,通過Looper.loop()
來開啟訊息迴圈。 - HandlerThread 的
run
方法是一個無限迴圈,當不需要使用時,要通過quit
或者quitSafely
方法來終止執行緒。 - HandlerThread 的一個典型使用場景就是 IntentService。
IntentService
-
IntentService 是一種特殊的抽象 Service,內部封裝了 HandlerThread 和 Handler,比較適合執行一些高優先順序的後臺任務,不容易被殺死。
@Override public void onCreate() { super.onCreate(); HandlerThread thread = new HandlerThread("IntentService[" + mName + "]"); thread.start(); mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); }
-
IntentService 呼叫
stopSelf(int startId)
方法來嘗試停止服務,會等待所有訊息都處理完畢後才真正終止服務。 - IntentService 的執行順序是按外界發起的順序去執行的。
Android 中的執行緒池
- 執行緒池的優點:
- 重用執行緒池中的執行緒,避免因為執行緒的建立和銷燬所帶來的效能開銷。
- 能有效控制執行緒池的最大併發數,避免大量執行緒之間因互相搶佔系統資源而導致的阻塞現象。
- 能夠對執行緒進行簡單的管理,並提供定時執行以及指定間隔迴圈執行等功能。
- Android 中執行緒池的概念源於 Java 中的 Executor,真正實現為 ThreadPoolExecutor。
ThreadPoolExecutor
ThreadPoolExecutor 常用構造方法如下:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)
- corePoolSize :執行緒池的核心執行緒數,預設情況下核心執行緒會一直存活。如果將 ThreadPoolExecutor 的
allowCoreThreadTimeOut
屬性設定為 true,閒置時間超過 keepAliveTime 會被終止。 - maximumPoolSize :執行緒池所能容納的最大執行緒數(核心執行緒 + 非核心執行緒),當活動執行緒數達到這個數值後,後續任務會被阻塞。
- keepAliveTime :非核心執行緒閒置時的超時時長,超過這個時長,非核心執行緒會被回收。當 ThreadPoolExecutor 的
allowCoreThreadTimeOut
屬性設定為 true 時,同樣作用於核心執行緒。 - unit :指定 keepAliveTime 引數的時間單位。
- workQueue :執行緒池中的任務佇列,通過執行緒池的
execute
方法提交的 Runnable 物件會被儲存在這個引數中。 - threadFactory :執行緒工廠,為執行緒池提供建立新執行緒的功能。
ThreadPoolExecutor 執行任務時大致遵循如下規則:
- 如果執行緒池中的執行緒數量未達到核心執行緒的數量,那麼會直接啟動一個核心執行緒來執行任務。
- 如果執行緒池中的執行緒數量達到或超過核心執行緒的數量,那麼任務會被插入任務佇列等待執行。
- 如果步驟2中無法將任務插入任務佇列中,這往往是由於任務佇列已滿,這時如果執行緒數量未到達執行緒池規定的最大值,那麼會立即啟動一個非核心執行緒來執行任務。
- 如果步驟3中的執行緒數量已經達到了執行緒池規定的最大值,那麼就會拒絕執行此任務。
執行緒池的分類
Android 中最常見的四類執行緒池都是通過 Executors 的靜態方法建立的。
-
FixedThreadPool- 執行緒數量固定的執行緒池,只有核心執行緒
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
-
CachedThreadPool- 執行緒數量不固定的執行緒池,只有非核心執行緒
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
-
ScheduledThreadPool- 核心執行緒數量固定,非核心執行緒數量沒有限制的執行緒池,主要用於執行定時任務和具有固定週期的任務
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); } public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS, new DelayedWorkQueue()); }
-
SingleThreadExecutor- 只有一個核心執行緒的執行緒池,確保了所有的任務都在同一個執行緒中按順序執行
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }