《Android開發藝術探索》學習筆記之Android的執行緒和執行緒池
阿新 • • 發佈:2018-12-13
一、概述
1、主執行緒與子執行緒
- 主執行緒
- 又叫UI執行緒
- 主要作用是執行四大元件以及處理它們和使用者的互動,主要用來處理和介面相關的事情
- 子執行緒
- 執行耗時任務,比如網路請求、I/O操作等
- 網路訪問必須要在子執行緒中進行,否則網路訪問將會失敗並丟擲NetworkOnMainThreadException
- 為了避免被耗時操作所阻塞從而出現ANR現象
2、可以扮演執行緒角色的有
- (1)Thread
- (2)AsyncTask(底層使用執行緒池)
- 封裝了執行緒池和Handler
- 方便在子執行緒中更新UI
- (3)IntentService(底層使用執行緒)
- 是一個服務,內部採用HandlerThread來執行任務,任務執行完畢後IntentService會自動的退出
- (4)HandlerThread(底層使用執行緒)
- 是一種具有訊息迴圈的執行緒,內部可以使用Handler
3、在作業系統中
- 執行緒是作業系統排程的最小單元
- 執行緒是一種受限的系統資源,不可能無限制的產生
- 執行緒的建立和銷燬有相應的開銷
- 系統會通過時間片輪轉的方式排程每一個執行緒。除非執行緒數量小於或等於CPU的核心數
4、執行緒池
- 執行緒池會快取一定數量的執行緒,避免因為頻繁建立和銷燬執行緒所帶來的系統開銷
- 主要通過Executor來派生特定型別的執行緒池
二、Android中的執行緒形態
1、AsyncTask
-
定義:是一種輕量級的非同步任務類,可以線上程池中執行後臺任務,然後把執行的進度和最終結果傳遞給主執行緒並在主執行緒更新UI
- 特別耗時的任務還是建議使用執行緒池
-
特性:抽象的泛型類
- 三個引數:不需要傳遞具體的引數,三個泛型引數可以用void代替
- Params:表示引數的型別
- Progress:表示後臺任務的執行進度的型別
- Result:表示後臺任務的返回結果的型別
- 四個方法
- onPreExecute():在主執行緒中執行,在非同步任務執行之前
- doInBackground(Params…params):在執行緒池中執行,用於執行非同步任務
- 在此方法中可呼叫publishProgress方法來更新任務的進度,publishProgress方法會呼叫onProgressUpdate方法
- 此方法需返回計算結果給onPostExecute方法
- onProgressUpdate(Progress…values):在主執行緒中執行,當後臺任務的執行進度發生改變時此方法會被呼叫
- onPostExecute(Result result):在主執行緒中執行,在非同步任務執行之後呼叫
- 另外AsyncTask還提供了 onCancelled() 方法,在主執行緒中執行,非同步任務被取消時會被呼叫,此時onPostExecute就不會被再呼叫
- Java中 … 表示引數的數量不定,是一種陣列型引數
- 三個引數:不需要傳遞具體的引數,三個泛型引數可以用void代替
-
使用:
- 自定義一個類繼承AsyncTask並重寫它的四個方法
- new 自定義類().execute(paramas, progress, result);
-
使用的條件:
- AsyncTask的類必須在主執行緒中載入
- AsyncTask的物件必須在主執行緒中建立
- 不要在程式中直接呼叫四個方法
- 一個AsynvTask物件只能執行一次,即只能呼叫一次execute方法
- 在Android3.0以後,預設情況下是序列執行的,但也可通過AsyncTask的executeOnExecutor來並行的執行任務
-
工作原理:
- execute -> executeOnExecutor
- 傳入的引數Executor實際上是一個序列的執行緒池,一個程序中所有的AsyncTask全部在這個序列的執行緒池中排隊執行
- executeOnExecutor 中呼叫 onPreExecute()
- 執行緒池中通過
protected synchronized void scheduleNext()
方法檢測是否有下一個任務,如果有就繼續呼叫execute方法 - AsyncTask中有兩個執行緒池和一個Handler
- 執行緒池SerialExecutor:用於任務的排隊
- 執行緒池THREAD_POOL_EXECUTOR:用於真正的執行任務
- InternalHandler:用於將執行環境從執行緒池切換到主執行緒
- 原始碼中InternalHandler的物件是靜態的,會在載入類的時候進行初始化,因此變相要求AsyncTask的類都必須在主執行緒中載入
- InternalHandler收到MESSAGE_POST_RESULT這個訊息後會呼叫Async的finish方法
private void finish(Result result) { if (isCancelled ()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }
- execute -> executeOnExecutor
2、HandlerThread
-
定義:是一種可以使用Handler的Thread,繼承了Thread
-
實現:
- 在run方法中通過 Looper.prerare() 來建立訊息佇列
- 通過 Looper.loop() 來開啟訊息迴圈
-
使用:
- HandlerThread的 run() 是一個無限迴圈,當不再使用HandlerThread時,可以通過quit或者quitSafely來終止執行緒的執行
- 在Android的一個具體的使用場景是IntentService
-
與普通Thread的區別:
- 普通Thread:主要用於在run方法中執行一個耗時任務
- HandlerThread:在內部建立了訊息佇列,外界需要通過Handler的訊息方式來通知HandlerThread執行一個具體的任務
3、IntentService
-
定義:是一種特殊的Service,用於執行耗時或高優先順序的後臺任務,繼承了Service並且是一個抽象類
- 當任務執行後會自動停止
- 因為是服務,所以優先順序比單純的執行緒要高,不容易被系統殺死
-
IntentService封裝了HandlerThread和Handler
-
每次啟動IntentService,它的onStratCommand方法就會被呼叫一次
- 使用者在此方法中處理每個後臺任務的Intent
- 原始碼在此方法中呼叫了onStart()方法
- onStart() -> onHandleIntent(),onHandleIntent()執行結束後,會呼叫stopSelf()或者stopSelf (stratId)來停止服務
- onHandleIntent()方法是一個抽象方法,需要在子類中實現
- 通過Intent的引數區分具體的任務並執行
- 使用者在此方法中處理每個後臺任務的Intent
-
多個後臺任務同時存在時,會按照外界發起的順序排隊執行
- 因為每執行一個後臺任務就必須啟動一次IntentService
- IntentService內部則通過訊息的方式向HandlerThread請求執行任務
- Handler中的Looper是順序處理訊息的
- 所以IntentService也是順序執行任務的
-
使用:
- 自定義一個類繼承IntentService
- 重寫onHandleIntent
- 重寫onDestroy
三、Android中的執行緒池
-
優點:
- (1)重用執行緒池中的執行緒,避免因為執行緒的建立和銷燬所帶來的效能開銷
- (2)能有效控制執行緒池的最大併發數,避免大量的執行緒之間因為互相搶佔系統資源而導致的阻塞現象
- (3)能夠對執行緒進行簡單的管理,並提供定時執行以及制定間隔迴圈執行等功能
-
Android的執行緒池概念主要來源於Java的Executor
- Executor是一個介面,真正的執行緒池實現是ThreadPoolExecutor
- ThreadPoolExecutor提供了一系列引數來配置不同的執行緒池
- 共有四種主要的執行緒池,都是通過Executors所提供的工廠方法來得到
1、ThreadPoolExecutor
-
定義:是執行緒池的真正實現,通過不同的構造方法引數來實現配置
-
常用構造方法(六個引數):
public ThreadPoolExecutor (int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory thradFactory)
- corePoolSize:執行緒的核心執行緒數
- 預設情況下,核心執行緒會線上程池中一直存活,即使他們處於閒置狀態
- 設定超時終止:將allowCoreThreadTimeOut屬性設定為true,當等待時間超過keepAliveTime所指定的時長後,核心執行緒就會被終止
- maximPoolSize:執行緒池所能容納的最大執行緒數。
- 當活動執行緒數達到這個數值後,後續的新任務將會被阻塞
- keepAliveTime:非核心執行緒閒置時的超時時長
- unit:用於制定keepAliveTime引數的時間單位
- 這是一個列舉,常用的有TimeUnit.MILLISECONDS、TimeUnit.SECONDS以及TimeUnit.MINUTES
- workQueue:執行緒池中的任務佇列
- 通過執行緒池的execute方法提交的Runnable物件會儲存在這個引數中
- threadFactory:執行緒工廠,是一個介面,為執行緒池提供建立新執行緒的功能
- 只有一個方法
Thread newThread(Runnable r)
- 只有一個方法
- corePoolSize:執行緒的核心執行緒數
-
另外還有一個不常用的引數RejectedExecutionHandler handler:
- 當執行緒池無法執行新任務時,可能由於佇列已滿或無法成功執行任務
- 此時ThreadPoolExecution會呼叫handler的rejectedExecution方法來通知呼叫者
- 預設情況下此方法會直接丟擲一個RejectedExecutionException
-
執行任務規則:
- (1)如果執行緒池中的執行緒數量未達到核心執行緒的數量,會直接啟動一個核心執行緒來執行任務
- (2)如果執行緒池中的執行緒數量已經達到或者超過核心執行緒的數量,那麼任務會被插入到任務佇列中排隊等待執行
- (3)如果步驟2中無法將任務插入到任務佇列中,往往是由於任務佇列已滿,這個時候如果執行緒數量未達到執行緒池規定的最大值,那麼會立刻啟動一個非核心執行緒來執行任務
- (4)如果步驟3中執行緒數量已經達到執行緒池規定的最大值,那麼就拒絕執行此任務,會呼叫rejectExecution方法來通知呼叫者
2、執行緒池的分類
-
(1)FixedThreadPool:
- 通過Executors的newFixedThreadPool方法建立
- 是一種執行緒數量固定的執行緒池
- 只有核心執行緒,空閒時也不會被回收,除非執行緒池被關閉
- 沒有超時機制
- 任務佇列也沒有大小限制
-
(2)CachedThreadPool:
- 適合執行大量耗時較少的任務
- 通過Executors的newCacheThreadPool方法建立
- 是一種執行緒數量不定的執行緒池
- 只有非核心執行緒,最大執行緒數為Integer.MAX_VALUE(相當於任意大)
- 空閒執行緒都有超時機制,時長為60秒
- 整個執行緒池都處於閒置狀態時,執行緒池中的執行緒都會因為超時而停止
- 任務佇列相當於一個空集合,任何任務都會被立刻執行,因此幾乎不佔用任何資源
-
(3)ScheduledTheadPool:
- 適合執行定時任務和具有固定週期的重複任務
- 通過Executors的newScheduledThreadPool方法來建立
- 核心執行緒數量固定,非核心執行緒數量不定
- 非核心執行緒空閒時會被立刻回收
-
(4)SingleThreadExecutor:
- 統一所有的外界任務到一個執行緒中,使得這些任務之間不需要處理執行緒同步的問題
- 通過Executors的newSingleThreadExecutor方法建立
- 內部只有一個核心執行緒,確保所有的任務都在同一個執行緒中按順序執行