1. 程式人生 > >《Android開發藝術探索》學習筆記之Android的執行緒和執行緒池

《Android開發藝術探索》學習筆記之Android的執行緒和執行緒池

一、概述

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中 表示引數的數量不定,是一種陣列型引數
  • 使用:

    • 自定義一個類繼承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;
      }
      
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的引數區分具體的任務並執行
  • 多個後臺任務同時存在時,會按照外界發起的順序排隊執行

    • 因為每執行一個後臺任務就必須啟動一次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)
  • 另外還有一個不常用的引數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方法建立
    • 內部只有一個核心執行緒,確保所有的任務都在同一個執行緒中按順序執行