在程序開發(fā)的實踐當中,為了讓程序表現(xiàn)得更加流暢,我們肯定會需要使用到多線程來提升程序的并發(fā)執(zhí)行性能。但是編寫多線程并發(fā)的代碼一直以來都是一個相對棘手的問題,所以想要獲得更佳的程序性能,我們非常有必要掌握多線程并發(fā)編程的基礎技能。
在匯川等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強發(fā)展的系統(tǒng)性、市場前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務理念,為客戶提供做網(wǎng)站、網(wǎng)站設計 網(wǎng)站設計制作按需設計網(wǎng)站,公司網(wǎng)站建設,企業(yè)網(wǎng)站建設,成都品牌網(wǎng)站建設,成都全網(wǎng)營銷,成都外貿(mào)網(wǎng)站建設,匯川網(wǎng)站建設費用合理。
眾所周知,Android 程序的大多數(shù)代碼操作都必須執(zhí)行在主線程,例如系統(tǒng)事件(例如設備屏幕發(fā)生旋轉(zhuǎn)),輸入事件(例如用戶點擊滑動等),程序回調(diào)服務,UI 繪制以及鬧鐘事件等等。那么我們在上述事件或者方法中插入的代碼也將執(zhí)行在主線程。
一旦我們在主線程里面添加了操作復雜的代碼,這些代碼就很可能阻礙主線程去響應點擊/滑動事件,阻礙主線程的 UI 繪制等等。我們知道,為了讓屏幕的刷新幀率達到 60fps,我們需要確保 16ms 內(nèi)完成單次刷新的操作。一旦我們在主線程里面執(zhí)行的任務過于繁重就可能導致接收到刷新信號的時候因為資源被占用而無法完成這次刷新操作,這樣就會產(chǎn)生掉幀的現(xiàn)象,刷新幀率自然也就跟著下降了(一旦刷新幀率降到 20fps 左右,用戶就可以明顯感知到卡頓不流暢了)。
為了避免上面提到的掉幀問題,我們需要使用多線程的技術(shù)方案,把那些操作復雜的任務移動到其他線程當中執(zhí)行,這樣就不容易阻塞主線程的操作,也就減小了出現(xiàn)掉幀的可能性。
那么問題來了,為主線程減輕負的多線程方案有哪些呢?這些方案分別適合在什么場景下使用?Android 系統(tǒng)為我們提供了若干組工具類來幫助解決這個問題。
AsyncTask: 為 UI 線程與工作線程之間進行快速的切換提供一種簡單便捷的機制。適用于當下立即需要啟動,但是異步執(zhí)行的生命周期短暫的使用場景。
HandlerThread: 為某些回調(diào)方法或者等待某些任務的執(zhí)行設置一個專屬的線程,并提供線程任務的調(diào)度機制。
ThreadPool: 把任務分解成不同的單元,分發(fā)到各個不同的線程上,進行同時并發(fā)處理。
IntentService: 適合于執(zhí)行由 UI 觸發(fā)的后臺 Service 任務,并可以把后臺任務執(zhí)行的情況通過一定的機制反饋給 UI。
了解這些系統(tǒng)提供的多線程工具類分別適合在什么場景下,可以幫助我們選擇合適的解決方案,避免出現(xiàn)不可預期的麻煩。雖然使用多線程可以提高程序的并發(fā)量,但是我們需要特別注意因為引入多線程而可能伴隨而來的內(nèi)存問題。舉個例子,在 Activity 內(nèi)部定義的一個 AsyncTask,它屬于一個內(nèi)部類,該類本身和外面的 Activity 是有引用關(guān)系的,如果 Activity 要銷毀的時候,AsyncTask 還仍然在運行,這會導致 Activity 沒有辦法完全釋放,從而引發(fā)內(nèi)存泄漏。所以說,多線程是提升程序性能的有效手段之一,但是使用多線程卻需要十分謹慎小心,如果不了解背后的執(zhí)行機制以及使用的注意事項,很可能引起嚴重的問題。
開線程無上限,就是開的越多,程序耗內(nèi)存越大/邏輯越混亂,很容易掛掉。影響有下面幾點:
內(nèi)存占用多
線程同步問題復雜,需要考慮一個變量或方法/實例被多線程同時調(diào)用的問題
線程的控制,停止與終止會很麻煩
在一個程序中,這些獨立運行的程序片斷叫作“線程”(Thread),利用它編程的概念就叫作“多線程處理”。多線程處理一個常見的例子就是用戶界面。利用線程,用戶可按下一個按鈕,然后程序會立即作出響應,而不是讓用戶等待程序完成了當前任務以后才開始響應。簡單地說,就是說可以有多個任務同時進行。
單線程在程序執(zhí)行時,所走的程序路徑按照連續(xù)順序排下來,前面的必須處理好,后面的才會執(zhí)行。因此,針對前面舉的例子,必須等待程序完成了當前任務以后才能開始相應。
使用多線程訪問公共的資源時,容易引發(fā)線程安全性問題,因此針對這種需要使用線程同步機制來保護公共的資源。
單線程較多線程來說,就不會出現(xiàn)上訴問題,系統(tǒng)穩(wěn)定、擴展性極強、軟件豐富。多用于點對點的服務。
本來開辟一個新的線程是屬于子線程,但是你的Handler是跟主線程綁定的。嚴格來說是在子線程中運行。
異步通信機制,將工作線程中需更新UI的操作信息 傳遞到 UI主線程,從而實現(xiàn) 工作線程對UI的更新處理,最終實現(xiàn)異步消息的處理。Handler不僅僅能將子線程的數(shù)據(jù)傳遞給主線程,它能實現(xiàn)任意兩個線程的數(shù)據(jù)傳遞。
(1)Message
Message 可以在線程之間傳遞消息??梢栽谒膬?nèi)部攜帶少量數(shù)據(jù),用于在不同線程之間進行數(shù)據(jù)交換。除了 what 字段,還可以使用 arg1 和 arg2 來攜帶整型數(shù)據(jù),使用 obj 來攜帶 Object 數(shù)據(jù)。
(2) Handler
Handler 作為處理中心,用于發(fā)送(sendMessage 系列方法)與處理消息(handleMessage 方法)。
(3) MessageQueue
MessageQueue 用于存放所有通過 Handler 發(fā)送的消息。這部分消息會一直存放在消息隊列中,直到被處理。每個線程中只會有一個 MessageQueue 對象
(4) Looper
Looper 用于管理 MessageQueue 隊列,Looper對象通過loop()方法開啟了一個死循環(huán)——for (;;){},不斷地從looper內(nèi)的MessageQueue中取出Message,并傳遞到 Handler 的 handleMessage() 方法中。每個線程中只會有一個 Looper 對象。
AsyncTask 是一種輕量級的任務異步類,可以在后臺子線程執(zhí)行任務,且將執(zhí)行進度及執(zhí)行結(jié)果傳遞給 UI 線程。
(1)onPreExecute()
在 UI 線程上工作,在任務執(zhí)行 doInBackground() 之前調(diào)用。此步驟通常用于設置任務,例如在用戶界面中顯示進度條。
(2)doInBackground(Params... params)
在子線程中工作,在 onPreExecute() 方法結(jié)束后執(zhí)行,這一步被用于在后臺執(zhí)行長時間的任務,Params 參數(shù)通過 execute(Params) 方法被傳遞到此方法中。任務執(zhí)行結(jié)束后,將結(jié)果傳遞給 onPostExecute(Result) 方法,同時我們可以通過 publishProgress(Progress) 方法,將執(zhí)行進度發(fā)送給 onProgressUpdate(Progress) 方法。
(3)onProgressUpdate(Progress... values)
在 UI 線程上工作,會在 doInBackground() 中調(diào)用 publishProgress(Progress) 方法后執(zhí)行,此方法用于在后臺計算仍在執(zhí)行時(也就是 doInBackgound() 還在執(zhí)行時)將計算執(zhí)行進度通過 UI 顯示出來。例如,可以通過動畫進度條或顯示文本字段中的日志,從而方便用戶知道后臺任務執(zhí)行的進度。
(4)onPostExecute(Result result)
在 UI 線程上工作,在任務執(zhí)行完畢(即 doInBackground(Result) 執(zhí)行完畢)并將執(zhí)行結(jié)果傳過來的時候工作。
使用規(guī)則:
(1)AsyncTask 是個抽象類,所以要創(chuàng)建它的子類實現(xiàn)抽象方法
(1)AsyncTask 類必須是在 UI 線程中被加載,但在Android 4.1(API 16)開始,就能被自動加載完成。
(2)AsyncTask 類的實例對象必須在 UI 線程中被創(chuàng)建。
(3)execute() 方法必須是在 UI 線程中被調(diào)用。
(4)不要手動調(diào)用方法 onPreExecute()、onPostExecute()、doInBackground()、onProgressUpdate()
(5)任務只能執(zhí)行一次(如果嘗試第二次執(zhí)行,將拋出異常)。即一個AsyncTask對象只能調(diào)用一次execute()方法。
原理:
? ? ? 其源碼中原理還是 Thread 與 Handler 的實現(xiàn),其包含 兩個線程池,一個 Handler,如下所示:
名稱類型作用
SERIAL_EXECUTOR線程池分發(fā)任務,串行分發(fā),一次只分發(fā)一個任務
THREAD_POOL_EXECUTOR線程池執(zhí)行任務,并行執(zhí)行,執(zhí)行的任務由 SERIAL_EXECUTOR 分發(fā)
InternalHandlerHandler負責子線程與主線程的溝通,通知主線程做 UI 工作
一方面減少了每個并行任務獨自建立線程的開銷,另一方面可以管理多個并發(fā)線程的公共資源,從而提高了多線程的效率。所以ThreadPoolExecutor比較適合一組任務的執(zhí)行。Executors利用工廠模式對ThreadPoolExecutor進行了封裝。
Executors提供了四種創(chuàng)建ExecutorService的方法,他們的使用場景如下:
1. Executors.newFixedThreadPool()
創(chuàng)建一個定長的線程池,每提交一個任務就創(chuàng)建一個線程,直到達到池的最大長度,這時線程池會保持長度不再變化。
當線程處于空閑狀態(tài)時,它們并不會被回收,除非線程池被關(guān)閉。當所有的線程都處于活動狀態(tài)時,新任務都會處于等待狀態(tài),直到有線程空閑出來。
只有核心線程并且不會被回收,能夠更加快速的響應外界的請求。
2. Executors.newCachedThreadPool()
創(chuàng)建一個可緩存的線程池,如果當前線程池的長度超過了處理的需要時,它可以靈活的回收空閑的線程,當需要增加時,它可以靈活的添加新的線程,而不會對池的長度作任何限制
線程數(shù)量不定的線程池,只有非核心線程,最大線程數(shù)為 Integer.MAX_VALUE。當線程池中的線程都處于活動狀態(tài)時,線程池會創(chuàng)建新的線程來處理新任務,否則利用空閑的線程來處理新任務。線程池中的空閑線程具有超時機制,為 60s。
任務隊列相當于一個空集合,導致任何任務都會立即被執(zhí)行,適合執(zhí)行大量耗時較少的任務。當整個線程池都處于限制狀態(tài)時,線程池中的線程都會超時而被停止。
3. Executors.newScheduledThreadPool()
創(chuàng)建一個定長的線程池,而且支持定時的以及周期性的任務執(zhí)行,類似于Timer。
非核心線程數(shù)沒有限制,并且非核心線程閑置的時候立即回收,主要用于執(zhí)行定時任務和具有固定周期的重復任務。
4. Executors.newSingleThreadExecutor()
創(chuàng)建一個單線程化的executor,它只創(chuàng)建唯一的worker線程來執(zhí)行任務
只有一個核心線程,保證所有的任務都在一個線程中順序執(zhí)行,意義在于不需要處理線程同步的問題。
一般用于執(zhí)行后臺耗時任務,當任務執(zhí)行完成會自動停止;同時由于它是一個服務,優(yōu)先級要遠遠高于線程,更不容易被系統(tǒng)殺死,因此比較適合執(zhí)行一些高優(yōu)先級的后臺任務。
使用步驟:創(chuàng)建IntentService的子類,重寫onHandleIntent方法,在onHandleIntent中執(zhí)行耗時任務
原理:在源碼實現(xiàn)上,IntentService封裝了HandlerThread和Handler。onHandleIntent方法結(jié)束后會調(diào)用IntentService的stopSelf(int startId)方法嘗試停止服務。
IntentService的內(nèi)部是通過消息的方式請求HandlerThread執(zhí)行任務,HandlerThread內(nèi)部又是一種使用Handler的Thread,這就意味著IntentService和Looper一樣是順序執(zhí)行后臺任務的
(HandlerThread:封裝了Handler + ThreadHandlerThread適合在有需要一個工作線程(非UI線程)+任務的等待隊列的形式,優(yōu)點是不會有堵塞,減少了對性能的消耗,缺點是不能同時進行多個任務的處理,需要等待進行處理。處理效率低,可以當成一個輕量級的線程池來用)
文章名稱:android多線程開發(fā),安卓多線程有幾種實現(xiàn)方法
文章鏈接:http://aaarwkj.com/article32/dssjipc.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供小程序開發(fā)、網(wǎng)站建設、網(wǎng)站收錄、網(wǎng)站設計公司、企業(yè)建站、網(wǎng)站排名
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)