Android多線程面試---談談你對多線程的理解+handle機制

要談線程,就不得不引出進程的概念,在Android中一個應用程序就是一個單獨的進程,一般來說,當我們運行一個應用,系統就會自動創建一個進程,並且爲這個進程創建一個主線程--UI線程,這樣就可以運行MainActivity,應用程序的組件默認都是運行在這個進程中,當然我們可以指定Android四大組件的運行進程,在清單文件中對應的組件通過設置屬性---android:process來使組件運行在不同的進程中,Android的多進程有利也有弊:

  1. 優勢:將應用的組件拆分在不同的進程中,可以使主線程得到更多的內存容量,因爲Android系統爲每個應用程序進程分配的內存是有限量的,應用程序的進程使用內存超過了這個容量,就會報 oom 也就是內存泄漏,這是致命的bug
  2. 壞處:每個進程都有自己的虛擬機實例,將一個應用程序分置在不同的進程中,會使數據在進程間的通訊變得很麻煩,當然我們可以通過進程間的通訊來解決這個問題,進程間通訊方法:AIDL、文件(磁盤)、Binder、Messenger、ContentProvider(內容提供者)、Socket,因爲這篇文章是講線程,所以進程以後再說,

上述就是Android中進程的一些基本知識點,有了進程的概念,就可以談線程,線程是操作系統能夠進行運算調度的最小單位,線程是進程的子集,線程可以並行的執行不同任務,所有的線程共享同一片內存空間,這就爲線程間通信提供了基礎,線程有五種狀態:創建,就緒,運行,阻塞,死亡;

有些面試會問到,線程的start()和run()是什麼區別?start是啓動一個線程,run是讓這個線程運行,很明顯就是start在創建一個新的線程,而run是操作當前這個線程去運行。

當應用程序,也就是進程中有組件在運行的時候,應用程序的主線程UI線程肯定是在運行狀態的,默認情況下,應用的所有組件都是在UI線程完成的,包括響應用戶的操作,組件的生命週期方法的調用,UI更新等等,因此在UI線程中執行耗時操作後,線程阻塞超過5秒,程序就會處於ANR的狀態,application not response,這是絕對不能出現的bug,所以我們需要在工作線程中做各種耗時操作,但是更新UI的操作一定要在UI線程中,這是因爲更新UI是通過調用invalidate()方法重繪界面來實現的,而invalidate()方法是非線程安全的,如果我們在一個線程更新了UI,有可能其他線程也在更新UI,這時候就出現了顯示不同步的數據錯亂!!

在Android中,我們將除了UI線程的其他所有線程都稱爲工作線程,也就是說Android中只有兩種線程:UI線程、工作線程,所以我們要在工作線程做耗時操作(讀寫數據庫,文件,訪問網絡請求等),而在UI線程刷新頁面,這就要求我們掌握線程間的通訊,Android系統提供的線程通訊有兩種:AsyncTask機制、Handler機制

AsyncTask機制:

AsyncTask是一個異步任務,在UI線程運行的時候,可以在後臺執行一些異步的操作,在不使用工作線程或者Handler的時候,它在後臺執行將結果返回給UI線程,AsyncTask的優點是代碼邏輯簡潔易讀易維護,但是它不能去運行耗時特別長的任務,適合執行幾秒內結束的操作,長時間執行的任務應該交給工作線程或者是Handler機制去執行,而且一次執行後就結束了,沒有提供延時、循環的任務,那麼AsyncTask使用是怎麼一回事?

AsyncTask只能通過繼承來使用,也就是新建一個類去繼承AsyncTask,大概重寫它的四個方法就夠用了:

  1. 任務執行前                      ---onProExecute(),比如執行正在下載的窗口,UI線程
  2. 開啓工作線程,執行任務---doInBackGroud(),後臺執行,工作線程
  3. 實時返回進度條的數據    ---onProgressUpdate(),更新後臺任務的進度,UI線程
  4. 任務執行完成                  ---onPostExecute(),返回任務結果,UI線程

在使用AsyncTask時,需要注意幾個點:

  1. AsyncTask必須在UI線程中加載
  2. 必須在UI線程中實例化
  3. 不要手動去調用這些方法,交給系統
  4. 啓動執行AsyncTask任務的execute方法,必須在UI線程調用
  5. AsyncTask任務只能執行一次

Handler機制:

Handler機制,也就是通過Handler,Looper,MessageQueue和Message這幾個類協調來完成,首先看看Handler機制實現線程通信的原理,先說說這幾個類的作用:

  1. handler:發送和接收處理Message對象和Runnable對象
  2. Looper:用MessageQueue中取出Message對象,通過匹配Taget發給Handler處理
  3. MessageQueue:消息隊列,用於存放通過Handler發佈的消息
  4. Message:消息的類型,包含一些實例對象

一個Handler對象僅與一個Looper相關聯,一個Message也僅與一個目標Handler對象相關聯,一個Looper對象擁有一個MessageQueue,多個不同的Handler對象可以與同一個Message相關聯,例如UI線程中創建的Handler對象發消息時都會發送到同一個Looper中,也會放到同一個MessageQueue裏邊,這就是Handler機制實現多線程通信的核心。

剛剛說一個Handler對象僅與一個Looper相關聯,那是在什麼時候去關聯的呢?是在創建Handler對象的時候,UI線程在創建的時候就會擁有自己的一個handler對象和一個Looper對象(工作線程在創建Handler對象的時候,首先要調用Looper.prepare()來生成工作線程對應的Looper,不然會拋異常),然後在UI主線程中的任何地方創建的handler都會默認的指向UI主線程的Looper對象,和這個Looper對象相關聯,然後我們將Handler對象傳遞到任意需要的工作線程中,這時候工作線程所使用的Handler對象處理的消息就是在UI主線程的Looper對象中,也就是在UI主線程的MessageQueue中,從而達到線程間的通信。

實質上,Android的多線程就是將不同的Message放到同一個MessageQueue,也就是說多個線程間共享一個MessageQueue,然後UI主線程或者工作線程等待MessageQueue取出Message,交還給不同線程下的Handler去處理消息

我們再從源碼層面分析一下handler,先整理幾個問題?

  1. Looper類是用來存放MessageQueue,然後控制msg的入隊和出隊,

 

IntentService,與普通service不同的是在oncreate時開啓一個線程

 

線程同步的機制:

  • synchronize方法或者同步代碼塊都可以
  • 在子線程run方法里加lock(),最後finally的時候unlock(),最好是在循環體之前加鎖

 

多線程通信:

  • Handler
  • 廣播
  • EventBus
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章