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

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

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

一.概述

1.含義:線程是CPU調度的最小單元。

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

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

3.分類:

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

二.線程形態

對于主線程和子線程相信已經非常熟悉了,現在主要學習以下三種形態的線程:

1.AsyncTask

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

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

  • 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四種線程池的使用


希望這篇文章對你有幫助~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,818評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,185評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,656評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,647評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,446評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,951評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,041評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,189評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,718評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,800評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,419評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,420評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,755評論 2 371

推薦閱讀更多精彩內容

  • Android中的線程 線程,在Android中是非常重要的,主線程處理UI界面,子線程處理耗時操作。如果在主線程...
    shenhuniurou閱讀 764評論 0 3
  • 從用途上來說,線程分為主線程和子線程,主線程主要處理和界面相關的事情,子線程則往往用于執行耗時操作。 除了Thre...
    小柏不是大白閱讀 637評論 0 3
  • 主席(任玉剛)當時在他群里發這書簡介的時候,只看目錄就知道是我想要的,哈哈,書還是有些難度的,不過真是一本超棒的書...
    HuDP閱讀 5,762評論 14 69
  • 詩歌的感覺 陽春三月,北方的雪花終于離去, 春風一抹,吹醒了多少詩人的思緒, 松軟的泥土孕育了嫩綠的草芽, 詩歌的...
    子涵周雨閱讀 263評論 0 0
  • 在一個寒冷冬天的晚上,小明用自己兼職掙來的200元錢,去請他心儀的女生小花吃火鍋。火鍋的蒸蒸熱氣,溫暖著兩個漂泊在...
    斑點魚閱讀 639評論 0 0