要點提煉|開發藝術之線程

本篇的主要內容是Android的線程和線程池:

  • 概述
  • 線程形態
    • AsyncTask
    • HandlerThread
    • IntentService
  • 線程池

一.概述java

1.含義:線程是操做系統調度的最小單元。緩存

2.特色:線程是一種受限的系統資源。即線程不可無限制的產生且線程的建立和銷燬都有必定的開銷。bash

Q:如何避免頻繁建立和銷燬線程所帶來的系統開銷? A:採用線程池,池中會緩存必定數量的線程,進而達到效果。多線程

3.分類:併發

  • 按用途可分爲兩類:
    • 主線程:通常一個進程只有一個線程,主要處理界面交互相關的邏輯。
    • 子線程:除主線程以外都是子線程,主要用於執行耗時操做
  • 按形態可分爲三類:
    • AsyncTask:底層封裝了線程池和Handler,便於執行後臺任務以及在子線程中進行UI操做。
    • HandlerThread:一種具備消息循環的線程,其內部可以使用Handler。
    • IntentService:是一種異步、會自動中止的服務,內部採用HandlerThread。

image


二.線程形態異步

對於主線程和子線程相信已經很是熟悉了,如今主要學習如下三種形態的線程:ide

1.AsyncTaskoop

a.AsyncTask:一種輕量級的異步任務類。源碼分析

在Android中實現異步任務機制有兩種方式:Handler和AsyncTask。post

  • Handler機制存在的問題:代碼相對臃腫;多任務同時執行時不易精確控制線程。
  • 引入AsyncTask的好處:建立異步任務更簡單,直接繼承它可方便實現後臺異步任務的執行和進度的回調更新UI,而無需編寫任務線程和Handler實例就能完成相同的任務。

b.AsyncTask是抽象的泛型類,其組成成員有:

  • 三個泛型參數:
    • Params:表示執行AsyncTask須要傳入的參數,可用於在後臺任務中使用;
    • Progress:表示後臺任務執行的進度;
    • Result: 表示後臺任務的返回結果的類型;
    • 若沒有傳遞具體的參數,這三個泛型參數均可使用void。如:
//含義:在執行AsyncTask時不須要傳入參數給後臺任務、使用整型數據來做爲進度顯示單位,最後使用布爾型數據來反饋執行結果
public abstract class AsyncTask<Void, Integer, Boolean>
複製代碼
  • 五個核心方法:
    • onPreExecute()
      • 運行在:主線程
      • 調用時刻:在異步任務執行以前被調用
      • 做用:可用於進行一些界面上的初始化操做
    • doInBackground(Params…params)
      • 運行在:子線程
      • 做用:可用於處理全部的耗時任務。若須要更新UI需調用 publishProgress(Progress...)方法
      • 注意:任務一旦完成就經過return語句將任務的執行結果返回,若Result被指定爲void,就可不返回執行結果
    • onProgressUpdate(Progress…values)
      • 運行在:主線程
      • 調用時刻:在後臺任務中調用publishProgress(Progress...)以後該方法會被調用
      • 做用:可利用方法中攜帶的參數如Progress來對UI進行相應地更新
    • onPostExecute(Result result)
      • 運行在:主線程
      • 調用時刻:在異步任務執行完畢並經過return語句返回時被調用
      • 做用:可利用方法中返回的數據來進行一些UI操做
    • onCancelled()
      • 運行在:主線程
      • 調用時刻:當異步任務被取消時被調用
      • 做用:可用於作界面取消的更新
    • 注意:
      • 不要直接調用onPreExecute()、doInBackground()、onProgressUpdate()、onPostExecute)和onCancelled()方法
      • AsyncTask對象必須在主線程建立
  • 開始和結束異步任務的方法:
    • execute(Params...params)
      • 必須在主線程中調用
      • 做用:表示開始一個異步任務
      • 注意:一個異步對象只能調用一次execute()方法
    • cancel(booleanmayInterruptIfRunning)
      • 必須在主線程中調用
      • 做用:表示中止一個異步任務

好比自定義一個AsyncTask,來模擬一個下載任務:

class DownloadTask extends AsyncTask<Void, Integer, Boolean> {  
  
    @Override//初始化一個ProgressDialog  
    protected void onPreExecute() {  
        progressDialog.show();  
    }  
  
    @Override//具體的下載邏輯
    protected Boolean doInBackground(Void... params) {  
        try {  
            while (true) {  
                int downloadPercent = doDownload();  
                publishProgress(downloadPercent);  
                if (downloadPercent >= 100) {  
                    break;  
                }  
            }  
        } catch (Exception e) {  
            return false;  
        }  
        return true;  
    }  
  
    @Override//顯示當前的下載進度
    protected void onProgressUpdate(Integer... values) {  
        progressDialog.setMessage("當前下載進度:" + values[0] + "%");  
    }  
  
    @Override//提示任務的執行結果  
    protected void onPostExecute(Boolean result) {  
        progressDialog.dismiss();  
        if (result) {  
            Toast.makeText(context, "下載成功", Toast.LENGTH_SHORT).show();  
        } else {  
            Toast.makeText(context, "下載失敗", Toast.LENGTH_SHORT).show();  
        }  
    }  
}  
複製代碼

任務的啓動和中止只須要如下幾行代碼:

// 開始任務  
DownloadTask mDownloadTask  = new DownloadTask();  
mDownloadTask .execute();  
   
// 中止任務  
mDownloadTask .cancel(true);  
複製代碼

補充實例詳解Android中AsyncTask的使用

c.工做原理

  • 內部有一個靜態的Handler對象即InternalHandler
    • 做用:將執行環境從線程池切換到主線程;經過它來發送任務執行的進度以及執行結束等消息。
    • 注意:必須在主線程中建立
  • 內部有兩個線程池:
    • SerialExecutor:用於任務的排隊,默認是串行的線程池
    • THREAD_POOL_EXECUTOR:用於真正執行任務。
  • 排隊執行過程:
    • 把參數Params封裝爲FutureTask對象,至關於Runnable;
    • 調用SerialExecutor.execute()將FutureTask插入到任務隊列tasks;
    • 若沒有正在活動的AsyncTask任務,則就會執行下一個AsyncTask任務。執行完畢後會繼續執行其餘任務直到全部任務都完成。即默認使用串行方式執行任務。

注意:AsyncTask不適用於進行特別耗時的後臺任務,而是建議用線程池。

推薦閱讀Android AsyncTask徹底解析,帶你從源碼的角度完全理解AsyncTask原理及不足


2.HandlerThread

a.HandlerThread是一個線程類,它繼承自Thread

與普通Thread的區別:具備消息循環的效果。原理:

  • 內部HandlerThread.run()方法中有Looper,經過Looper.prepare()來建立消息隊列,並經過Looper.loop()來開啓消息循環。

b實現方法

  • 實例化一個HandlerThread對象,參數是該線程的名稱;
  • 經過 HandlerThread.start()開啓線程;
  • 實例化一個Handler並傳入HandlerThread中的looper對象,使得與HandlerThread綁定;
  • 利用Handler便可執行異步任務;
  • 當不須要HandlerThread時,經過HandlerThread.quit()/quitSafely()方法來終止線程的執行。
private HandlerThread myHandlerThread ;  
private Handler handler ;  
@Override  
protected void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  
   setContentView(R.layout.activity_main);  
   //實例化HandlerThread
   myHandlerThread = new HandlerThread("myHandler") ;  
   //開啓HandlerThread
   myHandlerThread.start();  
   //將Handler對象與HandlerThread線程綁定
   handler =new Handler(myHandlerThread.getLooper()){  
       @Override  
        publicvoid handleMessage(Message msg) {  
           super.handleMessage(msg);  
            // 這裏接收Handler發來的消息,運行在handler_thread線程中  
            //TODO...  
        }  
    };  
   
   //在主線程給Handler發送消息  
   handler.sendEmptyMessage(1) ;  
   new Thread(new Runnable() {  
       @Override  
        publicvoid run() {  
           //在子線程給Handler發送數據  
           handler.sendEmptyMessage(2) ;  
        }  
    }).start();  
}  
@Override  
protected void onDestroy() {  
   super.onDestroy();  
   //終止HandlerThread運行
   myHandlerThread.quit() ;  
}  
複製代碼

補充實例Android 多線程之HandlerThread 徹底詳解

c.用途:

  • 進行串行異步通訊
  • 構造IntentService

在以前學習Handler機制時知道在子線程使用Handler的方法,其實HandlerThread的出現使得這一過程變得更加簡便,更多解析見淺析HandlerThread


3.IntentService

a.IntentService是一個繼承自Service的抽象類

b.優勢:

  • 相比於線程:因爲是服務,優先級比線程高,更不容易被系統殺死。所以較適合執行一些高優先級的後臺任務。
  • 相比於普通Service:可自動建立子線程來執行任務,且任務執行完畢後自動退出

c.IntentService內部封裝了HandlerThread和Handler,工做原理:

  • IntentService.onCreate()裏建立一個Handle對象即HandlerThread,利用其內部的Looper會實例化一個ServiceHandler對象;
  • 任務請求的Intent會被封裝到Message並經過ServiceHandler發送給Looper的MessageQueue,最終在HandlerThread中執行;
  • ServiceHandler.handleMessage()中會調用IntentService.onHandleIntent(),可在該方法中處理後臺任務的邏輯。

流程圖

圖片來源Android IntentService的使用和源碼分析

d.使用方法:

  • 新建類並繼承IntentService,重寫onHandleIntent()方法,該方法:
    • 運行在:子線程,所以能夠去處理一些耗時操做。
    • 做用:從Intent參數中區分具體的任務並執行這些任務
  • 在配置文件中進行註冊。
  • 在活動中利用Intent實現IntentService的啓動:
Intent intent = new Intent(this, MyService.class);
intent.putExtra("xxx",xxx);  
startService(intent);//啓動服務
複製代碼

具體實例見Service篇之IntentService

注意:無需手動中止服務,onHandleIntent()執行結束以後,IntentService會自動中止。

推薦閱讀Android多線程:IntentService用法&源碼分析


三.線程池

1.優勢

  • 重用線程池中的線程,避免線程的建立和銷燬帶來的性能消耗;
  • 有效控制線程池的最大併發數,避免大量的線程之間因互相搶佔系統資源而致使阻塞現象;
  • 進行線程管理,提供定時/循環間隔執行等功能。
  • 線程池的概念來源:Java中的Executor,它是一個接口。
  • 線程池的真正實現:ThreadPoolExecutor,提供一系列參數來配置線程池。
//構造參數
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
複製代碼
  • corePoolSize:核心線程數
    • 默認狀況下,核心線程會在線程中一直存活。
    • 當設置ThreadPoolExecutor的allowCoreThreadTimeOut屬性爲
      • true:表示核心線程閒置超過超時時長,會被回收;
      • false:表示核心線程不會被回收,會在線程池中一直存活。
  • maximumPoolSize:最大線程數
    • 當活動線程數達到這個數值後,後續的任務將會被阻塞。
  • keepAliveTime:非核心線程超時時間
    • 超過這個時長,閒置的非核心線程就會被回收。
    • 當設置ThreadPoolExecutor的allowCoreThreadTimeTout屬性爲true時,keepAliveTime對核心線程一樣有效。
  • unit:用於指定keepAliveTime參數的時間單位
    • 單位有:TimeUnit.MILLISECONDS、TimeUnit.SECONDS、TimeUnit.MINUTES等;
  • workQueue:任務隊列
    • 經過線程池的execute()方法提交的Runnable對象會存儲在這個參數中。
  • threadFactory:線程工廠,可建立新線程
    • 是個接口,只有一個方法Thread newThread(Runnable r)
  • handler:在線程池沒法執行新任務時進行調度。

實例線程池的原理及實現

3.ThreadPoolExecutor的默認工做策略

  • 若程池中的線程數量未達到核心線程數,則會直接啓動一個核心線程執行任務。
  • 若線程池中的線程數量已達到或者超過核心線程數量,則任務會被插入到任務列表等待執行。
    • 若任務沒法插入到任務列表中,每每因爲任務列表已滿,此時若是
      • 線程數量未達到線程池最大線程數,則會啓動一個非核心線程執行任務;
      • 線程數量已達到線程池規定的最大值,則拒絕執行此任務,ThreadPoolExecutor會調用RejectedExecutionHandler的rejectedExecution方法來通知調用者。

4.ThreadPoolExecutor線程池的分類:

  • FixThreadPool
    • 含義:線程數量固定的線程池,全部線程都是核心線程,當線程空閒時不會被回收。
    • 特色:能快速響應外界請求。
  • CachedThreadPool
    • 含義:線程數量不定的線程池(最大線程數爲Integer.MAX_VALUE),只有非核心線程,空閒線程有超時機制,超時回收。
    • 特色:適合於執行大量的耗時較少的任務
  • ScheduledThreadPool
    • 含義:核心線程數量固定,非核心線程數量不定
    • 特色:定時任務和固定週期的任務。
  • SingleThreadExecutor
    • 含義:只有一個核心線程,可確保全部的任務都在同一個線程中按順序執行。
    • 特色:無需處理線程同步問題。

推薦閱讀Java四種線程池的使用


但願這篇文章對你有幫助~

相關文章
相關標籤/搜索