① android多線程的四種方式:Handler、AsyncTask、ThreadPoolExector、IntentService
非同步通信機制,將工作線程中需更新UI的操作信息 傳遞到 UI主線程,從而實現 工作線程對UI的更新處理,最終實現非同步消息的處理。Handler不僅僅能將子線程的數據傳遞給主線程,它能實現任意兩個線程的數據傳遞。
(1)Message
Message 可以在線程之間傳遞消息。可以在它的內部攜帶少量數據,用於在不同線程之間進行數據交換。除了 what 欄位,還可以使用 arg1 和 arg2 來攜帶整型數據,使用 obj 來攜帶 Object 數據。
(2) Handler
Handler 作為處理中心,用於發送(sendMessage 系列方法)與處理消息(handleMessage 方法)。
(3) MessageQueue
MessageQueue 用於存放所有通過 Handler 發送的消息。這部分消息會一直存放在消息隊列中,直到被處理。每個線程中只會有一個 MessageQueue 對象
(4) Looper
Looper 用於管理 MessageQueue 隊列,Looper對象通過loop()方法開啟了一個死循環——for (;;){},不斷地從looper內的MessageQueue中取出Message,並傳遞到 Handler 的 handleMessage() 方法中。每個線程中只會有一個 Looper 對象。
AsyncTask 是一種輕量級的任務非同步類,可以在後檯子線程執行任務,且將執行進度及執行結果傳遞給 UI 線程。
(1)onPreExecute()
在 UI 線程上工作,在任務執行 doInBackground() 之前調用。此步驟通常用於設置任務,例如在用戶界面中顯示進度條。
(2)doInBackground(Params... params)
在子線程中工作,在 onPreExecute() 方法結束後執行,這一步被用於在後台執行長時間的任務,Params 參數通過 execute(Params) 方法被傳遞到此方法中。任務執行結束後,將結果傳遞給 onPostExecute(Result) 方法,同時我們可以通過 publishProgress(Progress) 方法,將執行進度發送給 onProgressUpdate(Progress) 方法。
(3)onProgressUpdate(Progress... values)
在 UI 線程上工作,會在 doInBackground() 中調用 publishProgress(Progress) 方法後執行,此方法用於在後台計算仍在執行時(也就是 doInBackgound() 還在執行時)將計算執行進度通過 UI 顯示出來。例如,可以通過動畫進度條或顯示文本欄位中的日誌,從而方便用戶知道後台任務執行的進度。
(4)onPostExecute(Result result)
在 UI 線程上工作,在任務執行完畢(即 doInBackground(Result) 執行完畢)並將執行結果傳過來的時候工作。
使用規則:
(1)AsyncTask 是個抽象類,所以要創建它的子類實現抽象方法
(1)AsyncTask 類必須是在 UI 線程中被載入,但在Android 4.1(API 16)開始,就能被自動載入完成。
(2)AsyncTask 類的實例對象必須在 UI 線程中被創建。
(3)execute() 方法必須是在 UI 線程中被調用。
(4)不要手動調用方法 onPreExecute()、onPostExecute()、doInBackground()、onProgressUpdate()
(5)任務只能執行一次(如果嘗試第二次執行,將拋出異常)。即一個AsyncTask對象只能調用一次execute()方法。
原理:
其源碼中原理還是 Thread 與 Handler 的實現,其包含 兩個線程池,一個 Handler,如下所示:
名稱類型作用
SERIAL_EXECUTOR線程池分發任務,串列分發,一次只分發一個任務
THREAD_POOL_EXECUTOR線程池執行任務,並行執行,執行的任務由 SERIAL_EXECUTOR 分發
InternalHandlerHandler負責子線程與主線程的溝通,通知主線程做 UI 工作
一方面減少了每個並行任務獨自建立線程的開銷,另一方面可以管理多個並發線程的公共資源,從而提高了多線程的效率。所以ThreadPoolExecutor比較適合一組任務的執行。Executors利用工廠模式對ThreadPoolExecutor進行了封裝。
Executors提供了四種創建ExecutorService的方法,他們的使用場景如下:
1. Executors.newFixedThreadPool()
創建一個定長的線程池,每提交一個任務就創建一個線程,直到達到池的最大長度,這時線程池會保持長度不再變化。
當線程處於空閑狀態時,它們並不會被回收,除非線程池被關閉。當所有的線程都處於活動狀態時,新任務都會處於等待狀態,直到有線程空閑出來。
只有核心線程並且不會被回收,能夠更加快速的響應外界的請求。
2. Executors.newCachedThreadPool()
創建一個可緩存的線程池,如果當前線程池的長度超過了處理的需要時,它可以靈活的回收空閑的線程,當需要增加時,它可以靈活的添加新的線程,而不會對池的長度作任何限制
線程數量不定的線程池,只有非核心線程,最大線程數為 Integer.MAX_VALUE。當線程池中的線程都處於活動狀態時,線程池會創建新的線程來處理新任務,否則利用空閑的線程來處理新任務。線程池中的空閑線程具有超時機制,為 60s。
任務隊列相當於一個空集合,導致任何任務都會立即被執行,適合執行大量耗時較少的任務。當整個線程池都處於限制狀態時,線程池中的線程都會超時而被停止。
3. Executors.newScheledThreadPool()
創建一個定長的線程池,而且支持定時的以及周期性的任務執行,類似於Timer。
非核心線程數沒有限制,並且非核心線程閑置的時候立即回收,主要用於執行定時任務和具有固定周期的重復任務。
4. Executors.newSingleThreadExecutor()
創建一個單線程化的executor,它只創建唯一的worker線程來執行任務
只有一個核心線程,保證所有的任務都在一個線程中順序執行,意義在於不需要處理線程同步的問題。
一般用於執行後台耗時任務,當任務執行完成會自動停止;同時由於它是一個服務,優先順序要遠遠高於線程,更不容易被系統殺死,因此比較適合執行一些高優先順序的後台任務。
使用步驟:創建IntentService的子類,重寫onHandleIntent方法,在onHandleIntent中執行耗時任務
原理:在源碼實現上,IntentService封裝了HandlerThread和Handler。onHandleIntent方法結束後會調用IntentService的stopSelf(int startId)方法嘗試停止服務。
IntentService的內部是通過消息的方式請求HandlerThread執行任務,HandlerThread內部又是一種使用Handler的Thread,這就意味著IntentService和Looper一樣是順序執行後台任務的
(HandlerThread:封裝了Handler + ThreadHandlerThread適合在有需要一個工作線程(非UI線程)+任務的等待隊列的形式,優點是不會有堵塞,減少了對性能的消耗,缺點是不能同時進行多個任務的處理,需要等待進行處理。處理效率低,可以當成一個輕量級的線程池來用)
② android中什麼時候會選擇用廣播來進行線程間的通信
android中什麼時候會選擇用廣播來進行線程間的通信 Android 多線程 通信
線程中通信就不要用廣播了吧 進程中通信可以用廣播或者aidl
可是,這兩天看到的項目都是這么做的;然後,自己分析了下,覺得一下的理由也是可以成立的;
1.正常情況下我們選擇handler消息機制來進行單向的線程間的通信;(工作線程向主線程發送消息)
因為主線程有現成的handler,而工作線程沒有現成的handler,這樣的話,主線程將handler交給工作線程而讓工作線程將工作的結果交給主線程;
相反,工作線程中沒有現成的handler(事實上是沒有消息隊列,也就是handler沒有綁定到工作線程),那麼,如果開辟的話,代碼角度上是挺麻煩的(相對應廣播機制來說);
2.廣播機制本身就是雙向的(工作線程向主線程發送廣播,主線程向工作線程發送廣播);
//另外,對於像一個activity中通過fragment來進行界面的處理; 我們大多數情況下是採用廣播的機制來實現fragment中adapter的數據的更新;這樣做主要是考慮到工作線程的任務載入完成,而具體的對應刷新的activity可能還沒有啟動;
另外,基於介面隔離原則,如果用handler進行通信的話,則不能很好的滿足這一原則;
你要是周期比較長 用廣播好些吧
應該與周期關系不是很密切。最主要的原因是兩條線成是雙向通信。
Handler類似於P2P的通信。
廣播則類似於一個server端,用來處理分發不同線程的請求,從控制器的角度來說用廣播更好一點。
一般使用Handler的,多用於子線程處理事務,完成時告知主線程這一類的情況。
而類似樓主所說的多條線程之間需要頻繁交互的話,廣播是個很好的選擇,並且結構清晰,只是不知道廣播的性能與handler相比會怎麼樣。
③ Android中的線程池
線程池的好處
1、重用線程池中的線程,避免線程的創建與銷毀帶來的性能開銷
2、能有效控制線程池的最大並發數,避免大量線程因搶占資源而導致的阻塞
3、能對線程進行簡單的管理,提供定時或者指定間隔時間、循環執行等操作
線程池的概率來自於java的Executor介面,實現類是ThreadPoolExecutor, 它提供一系列的參數來配置線程池,以此構建不同的線程池。Android的線程池分4類,都是通過Executors所提供的工廠方法來得到。
ThreadPoolExecutor有四個構造函數,下面這個是最常用的
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnnable> workQueue, ThreadFactory threadFactory)
corePoolSize
線程池中的核心線程數,默認情況下核心線程會在線程池中一直存活,即使他們處於閑置狀態。如果設置ThreadPoolExecutor 中的allowCoreThreadTimeOut = true, 核心線程在等待新任務到來時有超時機制,時間超過keepAliveTime所指定的時間後,核心線程會終止。
maximumPoolSize
最大線程數
keepAliveTime
非核心線程閑置的超時時間,超過這個時間,非核心線程會被回收。核心線程則要看allowCoreThreadTimeOut屬性的值。
unit
時間單位
workQueue
線程池中的工作隊列
threadFactory
線程工廠,為線程池提供創建新線程的功能。
舉個例子,我們常用的okhttp內部也是使用了線程池,它的ThreadPoolExecutor主要是定義在Dispatcher類裡面。 使用的是CachedThreadPool。
executorService = ThreadPoolExecutor(0, Int.MAX_VALUE, 60, TimeUnit.SECONDS, SynchronousQueue(), ThreadFactory("okhttp Dispatcher", false))
1、FixedThreadPool
通過Executors的newFixedThreadPool()創建,這是一個線程數量固定的線程池,裡面所有的線程都是核心線程。
public static ExecutorService newFixedThreadPool(int nThreads){
return new ThreadPoolExecutor(nThreads, nThreads, 0, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())
}
2、CachedThreadPool
通過Executors的newCacheThreadPool()創建,這是一個線程數量不定的線程池,裡面所有的線程都是非核心線程。最大線程數是無限大,當線程池中的線程都處於活動狀態時,新的task會創建新的線程來處理,否則就使用空閑的線程處理,所有的線程都是60s的超時時間,超時後會自動回收。
public static ExecutorService newFixedThreadPool(){
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>())
}
3、ScheledThreadPool
通過Executors的newScheledThreadPool()創建, 核心線程固定,非核心線程無限大,當非核心線程空閑時,會立即被回收。適合做定時任務或者固定周期的重復任務。
public static ExecutorService newScheledThreadPool(int corePoolSize){
return new ThreadPoolExecutor(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.SECONDS, new DelayedWorkQueue())
}
4、SingleThreadExcecutor
通過Executors的newSingleThreadPool()創建,內部只有一個核心線程。
public static ExecutorService newFixedThreadPool(){
return new ThreadPoolExecutor(1, 1, 0, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())
}
課外知識:LinkedBlockingQueue
LinkedBlockingQueue是由鏈表組成的阻塞隊列,內部head 指向隊列第一個元素,last指向最後一個元素。入隊和出隊都會加鎖阻塞,都是使用了不同的鎖。
DelayedWorkQueue
延時隊列,隊內元素必須是Delayed的實現類。對內元素會按照Delayed時間進行排序,對內元素只有在delayed時間過期了才能出隊。
入隊的時候不阻塞隊列,出隊的時候,如果隊列為空或者隊列里所有元素都等待時間都沒有到期,則該線程進入阻塞狀態。
④ android中asynctask和thread的區別
Android平台很多應用使用的都是AsyncTask,而並非Thread和Handler去更新UI,這里Android123給大家說下他們到底有什麼區別,我們平時應該使用哪種解決方案。從Android 1.5開始系統將AsyncTask引入到android.os包中,過去在很早1.1和1.0 SDK時其實官方將其命名為UserTask,其內部是jdk 1.5開始新增的concurrent庫,做過j2ee的網友可能明白並發庫效率和強大性,比Java原始的Thread更靈活和強大,但對於輕量級的使用更為佔用系統資源。Thread是Java早期為實現多線程而設計的,比較簡單不支持concurrent中很多特性在同步和線程池類中需要自己去實現很多的東西,對於分布式應用來說更需要自己寫調度代碼,而為了Android UI的刷新google引入了Handler和Looper機制,它們均基於消息實現,有事可能消息隊列阻塞或其他原因無法准確的使用。 Android開發網推薦大家使用AsyncTask代替Thread+Handler的方式,不僅調用上更為簡單,經過實測更可靠一些,Google在Browser中大量使用了非同步任務作為處理耗時的I/O操作,比如下載文件、讀寫資料庫等等,它們在本質上都離不開消息,但是 AsyncTask相比Thread加Handler更為可靠,更易於維護,但AsyncTask缺點也是有的比如一旦線程開啟即 dobackground方法執行後無法給線程發送消息,僅能通過預先設置好的標記來控制邏輯,當然可以通過線程的掛起等待標志位的改變來通訊,對於某些應用Thread和Handler以及Looper可能更靈活。
⑤ 談談隊列和多線程的使用原理
在iOS中隊列分為以下幾種:
串列隊列:隊列中的任務只會順序執行;
dispatch_queue_tq = dispatch_queue_create("...", DISPATCH_QUEUE_SERIAL);
並行隊列: 隊列中的任務通常會並發執行;
dispatch_queue_tq = dispatch_queue_create("......",DISPATCH_QUEUE_CONCURRENT);
全局隊列:是系統的,直接拿過來(GET)用就可以;與並行隊列類似;
dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
主隊列:每一個應用程序對應唯一主隊列,直接GET即可;在多線程開發中,使用主隊列更新UI;
dispatch_queue_tq = dispatch_get_main_queue();
更多細節見下圖:
⑥ android 中,怎麼清除一個Handler里的所有消息隊列有什麼方法么
final void removeMessages(int what)
Remove any pending posts of messages with code 'what' that are in the message queue.
這個是我看API裡面的一個方法。看解釋應該是異常指定標識what對應的那個消息msg。。希望對你有幫助
⑦ 多線程任務隊列實現
我們在應用程序中, 要完成一些非同步工作, 同時在非同步工作中又要盡量少用switch case, 這時task任務繼承方式就可以達到效果。我們可以在作業隊列中分配或添加任務,然後作業隊列負責完成任務。這里的工作隊列可以是多線程的,也可以是單線程的。本文採用的是多線程的實現,單線程的可以留言給我。
設計一個多線程就可以參照線程池的框架, 它要包含幾個部分:
a. 線程池管理器:用於創建並管理線程池
b. 工作線程: 線程池中實際執行的線程
c. 任務介面:將任務抽象出來,形成任務介面,從而可以通過繼承來實現自己要做的事
d. 任務隊列:這里是通過個queue來保存。
本文完成的工作隊列是以跨平台為前提, 可以在多個平台上運行。並具有以下一些特點:
a. 作業隊列是可以單線程也可以多線程
b. 作業隊列有優先順序,具有高優先順序的作業將在隊列中具有較低優先順序的作業之前添加。因此,它們將在其他較低優先順序作業之前處理。
c. 作業隊列可以暫停,因此暫停時不能處理新作業。但是用戶仍然可以將作業添加到隊列中。一旦用戶選擇恢復作業隊列,作業將被處理。
我們實現的通用線程池框架由五個重要部分組成ZLThread,ZLWorkerThread,ZLThreadManager,ZLThreadPool,ZLTask,除此之外框架中還包括線程同步使用的類ZLMutex和ZLCondition,其中重要部分的大體聯系如下圖:
a. ZLTask是所有的任務的基類,其提供一個介面DoTaskProc,所有的任務類都必須從該類繼承,同時實現DoTaskProc方法。該方法中實現具體的任務邏輯。
b. ZLThread是線程的類抽象,其封裝了各個系統線程最經常使用的屬性和方法,是所有線程類的基類,具有一個介面Run。
c. ZLWorkerThread是實際被調度和執行的線程類,其從ZLThread繼承而來,實現了ZLThread中的Run方法。
d. ZLThreadPool是線程池類,其負責保存線程,釋放線程以及調度線程。
e. ZLThreadManager是線程池與用戶的直接介面,其屏蔽了內部的具體實現。
f. ZLMutex用於線程之間的互斥。
g. ZLCondition則是條件變數的封裝,用於線程之間的同步。
把線程與task任務進行統一管理,創建有限的線程數來處理task任務,如下圖:
從圖上可以看出主要含有三個隊列,任務隊列,工作線程隊列,忙碌線程隊列;任務隊列是一個阻塞隊列,任務不斷地被push進來,這里要有一個線程來取任務,獲取空閑線程, 交於空閑線程去處理,如果獲取到任務,則將線程會進入忙碌線程隊列中,執行任務的DoTaskProc工作,當工作完成,重新移出工作線程隊列。
http://blog.csdn.net/ithzhang/article/details/9020283
開源: https://git.coding.net/clzhan/MulitiThreadJob.git
⑧ Android線程池的使用
在Android中有主線程和子線程的區分。主線程又稱為UI線程,主要是處理一些和界面相關的事情,而子線程主要是用於處理一些耗時比較大的一些任務,例如一些網路操作,IO請求等。如果在主線程中處理這些耗時的任務,則有可能會出現ANR現象(App直接卡死)。
線程池,從名字的表明含義上我們知道線程池就是包含線程的一個池子,它起到新建線程、管理線程、調度線程等作用。
既然Android中已經有了線程的概念,那麼為什麼需要使用線程池呢?我們從兩個方面給出使用線程池的原因。
在Android中線程池就是ThreadPoolExecutor對象。我們先來看一下ThreadPoolExecutor的構造函數。
我們分別說一下當前的幾個參數的含義:
第一個參數corePoolSize為 核心線程數 ,也就是說線程池中至少有這么多的線程,即使存在的這些線程沒有執行任務。但是有一個例外就是,如果在線程池中設置了allowCoreThreadTimeOut為true,那麼在 超時時間(keepAliveTime) 到達後核心線程也會被銷毀。
第二個參數maximumPoolSize為 線程池中的最大線程數 。當活動線程數達到這個數後,後續添加的新任務會被阻塞。
第三個參數keepAliveTime為 線程的保活時間 ,就是說如果線程池中有多於核心線程數的線程,那麼在線程沒有任務的那一刻起開始計時,如果超過了keepAliveTime,還沒有新的任務過來,則該線程就要被銷毀。同時如果設置了allowCoreThreadTimeOut為true,該時間也就是上面第一條所說的 超時時間 。
第四個參數unit為 第三個參數的計時單位 ,有毫秒、秒等。
第五個參數workQueue為 線程池中的任務隊列 ,該隊列持有由execute方法傳遞過來的Runnable對象(Runnable對象就是一個任務)。這個任務隊列的類型是BlockQueue類型,也就是阻塞隊列,當隊列的任務數為0時,取任務的操作會被阻塞;當隊列的任務數滿了(活動線程達到了最大線程數),添加操作就會阻塞。
第六個參數threadFactory為 線程工廠 ,當線程池需要創建一個新線程時,使用線程工廠來給線程池提供一個線程。
第七個參數handler為 拒絕策略 ,當線程池使用有界隊列時(也就是第五個參數),如果隊列滿了,任務添加到線程池的時候的一個拒絕策略。
可以看到FixedThreadPool的構建調用了ThreadPoolExecutor的構造函數。從上面的調用中可以看出FixedThreadPool的幾個特點:
可以看到CacheThreadPool的構建調用了ThreadPoolExecutor的構造函數。從上面的調用中可以看出CacheThreadPool的幾個特點:
可以看到ScheledThreadPoolExecutor的構建調用了ThreadPoolExecutor的構造函數。從上面的調用中可以看出ScheledThreadPoolExecutor的幾個特點:
可以看到SingleThreadExecutor的構建調用了ThreadPoolExecutor的構造函數。從上面的調用中可以看出SingleThreadExecutor的幾個特點:
⑨ Android線程池ThreadPoolExecutor詳解
傳統的多線程是通過繼承Thread類及實現Runnable介面來實現的,每次創建及銷毀線程都會消耗資源、響應速度慢,且線程缺乏統一管理,容易出現阻塞的情況,針對以上缺點,線程池就出現了。
線程池是一個創建使用線程並能保存使用過的線程以達到復用的對象,簡單的說就是一塊緩存了一定數量線程的區域。
1.復用線程:線程執行完不會立刻退出,繼續執行其他線程;
2.管理線程:統一分配、管理、控制最大並發數;
1.降低因頻繁創建&銷毀線程帶來的性能開銷,復用緩存在線程池中的線程;
2.提高線程執行效率&響應速度,復用線程:響應速度;管理線程:優化線程執行順序,避免大量線程搶占資源導致阻塞現象;
3.提高對線程的管理度;
線程池的使用也比較簡單,流程如下:
接下來通過源碼來介紹一下ThreadPoolExecutor內部實現及工作原理。
線程池的最終實現類是ThreadPoolExecutor,通過實現可以一步一步的看到,父介面為Executor:
其他的繼承及實現關系就不一一列舉了,直接通過以下圖來看一下:
從構造方法開始看:
通過以上可以看到,在創建ThreadPoolExecutor時,對傳入的參數是有要求的:corePoolSize不能小於0;maximumPoolSize需要大於0,且需要大於等於corePoolSize;keepAliveTime大於0;workQueue、threadFactory都不能為null。
在創建完後就需要執行Runnable了,看以下execute()方法:
在execute()內部主要執行的邏輯如下:
分析點1:如果當前線程數未超過核心線程數,則將runnable作為參數執行addWorker(),true表示核心線程,false表示非核心線程;
分析點2:核心線程滿了,如果線程池處於運行狀態則往workQueue隊列中添加任務,接下來判斷是否需要拒絕或者執行addWorker();
分析點3:以上都不滿足時 [corePoolSize=0且沒有運行的線程,或workQueue已經滿了] ,執行addWorker()添加runnable,失敗則執行拒絕策略;
總結一下:線程池對線程創建的管理,流程圖如下:
在執行addWorker時,主要做了以下兩件事:
分析點1:將runnable作為參數創建Worker對象w,然後獲取w內部的變數thread;
分析點2:調用start()來啟動thread;
在addWorker()內部會將runnable作為參數傳給Worker,然後從Worker內部讀取變數thread,看一下Worker類的實現:
Worker實現了Runnable介面,在Worker內部,進行了賦值及創建操作,先將execute()時傳入的runnable賦值給內部變數firstTask,然後通過ThreadFactory.newThread(this)創建Thread,上面講到在addWorker內部執行t.start()後,會執行到Worker內部的run()方法,接著會執行runWorker(this),一起看一下:
前面可以看到,runWorker是執行在子線程內部,主要執行了三件事:
分析1:獲取當前線程,當執行shutdown()時需要將線程interrupt(),接下來從Worker內部取到firstTask,即execute傳入的runnable,接下來會執行;
分析2:while循環,task不空直接執行;否則執行getTask()去獲取,不為空直接執行;
分析3:對有效的task執行run(),由於是在子線程中執行,因此直接run()即可,不需要start();
前面看到,在while內部有執行getTask(),一起看一下:
getTask()是從workQueue內部獲取接下來需要執行的runnable,內部主要做了兩件事:
分析1:先獲取到當前正在執行工作的線程數量wc,通過判斷allowCoreThreadTimeOut[在創建ThreadPoolExecutor時可以進行設置]及wc > corePoolSize來確定timed值;
分析2:通過timed值來決定執行poll()或者take(),如果WorkQueue中有未執行的線程時,兩者作用是相同的,立刻返回線程;如果WorkQueue中沒有線程時,poll()有超時返回,take()會一直阻塞;如果allowCoreThreadTimeOut為true,則核心線程在超時時間沒有使用的話,是需要退出的;wc > corePoolSize時,非核心線程在超時時間沒有使用的話,是需要退出的;
allowCoreThreadTimeOut是可以通過以下方式進行設置的:
如果沒有進行設置,那麼corePoolSize數量的核心線程會一直存在。
總結一下:ThreadPoolExecutor內部的核心線程如何確保一直存在,不退出?
上面分析已經回答了這個問題,每個線程在執行時會執行runWorker(),而在runWorker()內部有while()循環會判斷getTask(),在getTask()內部會對當前執行的線程數量及allowCoreThreadTimeOut進行實時判斷,如果工作數量大於corePoolSize且workQueue中沒有未執行的線程時,會執行poll()超時退出;如果工作數量不大於corePoolSize且workQueue中沒有未執行的線程時,會執行take()進行阻塞,確保有corePoolSize數量的線程阻塞在runWorker()內部的while()循環不退出。
如果需要關閉線程池,需要如何操作呢,看一下shutdown()方法:
以上可以看到,關閉線程池的原理:a. 遍歷線程池中的所有工作線程;b. 逐個調用線程的interrupt()中斷線程(註:無法響應中斷的任務可能永遠無法終止)
也可調用shutdownNow()來關閉線程池,二者區別:
shutdown():設置線程池的狀態為SHUTDOWN,然後中斷所有沒有正在執行任務的線程;
shutdownNow():設置線程池的狀態為STOP,然後嘗試停止所有的正在執行或暫停任務的線程,並返回等待執行任務的列表;
使用建議:一般調用shutdown()關閉線程池;若任務不一定要執行完,則調用shutdownNow();
總結一下:ThreadPoolExecutor在執行execute()及shutdown()時的調用關系,流程圖如下:
線程池可以通過Executors來進行不同類型的創建,具體分為四種不同的類型,如下:
可緩存線程池:不固定線程數量,且支持最大為Integer.MAX_VALUE的線程數量:
1、線程數無限制
2、有空閑線程則復用空閑線程,若無空閑線程則新建線程
3、一定程度上減少頻繁創建/銷毀線程,減少系統開銷
固定線程數量的線程池:定長線程池
1、可控制線程最大並發數(同時執行的線程數)
2、超出的線程會在隊列中等待。
單線程化的線程池:可以理解為線程數量為1的FixedThreadPool
1、有且僅有一個工作線程執行任務
2、所有任務按照指定順序執行,即遵循隊列的入隊出隊規則
定時以指定周期循環執行任務
一般來說,等待隊列 BlockingQueue 有: ArrayBlockingQueue 、 LinkedBlockingQueue 與 SynchronousQueue 。
假設向線程池提交任務時,核心線程都被佔用的情況下:
ArrayBlockingQueue :基於數組的阻塞隊列,初始化需要指定固定大小。
當使用此隊列時,向線程池提交任務,會首先加入到等待隊列中,當等待隊列滿了之後,再次提交任務,嘗試加入隊列就會失敗,這時就會檢查如果當前線程池中的線程數未達到最大線程,則會新建線程執行新提交的任務。所以最終可能出現後提交的任務先執行,而先提交的任務一直在等待。
LinkedBlockingQueue :基於鏈表實現的阻塞隊列,初始化可以指定大小,也可以不指定。
當指定大小後,行為就和 ArrayBlockingQueue一致。而如果未指定大小,則會使用默認的 Integer.MAX_VALUE 作為隊列大小。這時候就會出現線程池的最大線程數參數無用,因為無論如何,向線程池提交任務加入等待隊列都會成功。最終意味著所有任務都是在核心線程執行。如果核心線程一直被占,那就一直等待。
SynchronousQueue :無容量的隊列。
使用此隊列意味著希望獲得最大並發量。因為無論如何,向線程池提交任務,往隊列提交任務都會失敗。而失敗後如果沒有空閑的非核心線程,就會檢查如果當前線程池中的線程數未達到最大線程,則會新建線程執行新提交的任務。完全沒有任何等待,唯一制約它的就是最大線程數的個數。因此一般配合Integer.MAX_VALUE就實現了真正的無等待。
但是需要注意的是, 進程的內存是存在限制的,而每一個線程都需要分配一定的內存。所以線程並不能無限個。
⑩ android 多個子線程有多個loop嗎
handlerB當然不能接收到taskA中的handlerA發送的message,可能有同學會說了,handlerA和handlerB都是在主線程中創建的handler,他們都關聯於主線程,每個線程都有一個隊列messageQueue,looper管理這個隊列並且分發消息,無論是handlerA還是handlerB都是發送消息到主線程中的messageQueue, 並且這兩個handler的代碼也是一樣的,handlerA所在的視圖處於後台,視圖B在前台,handlerB應該能夠接受handlerA發送的消息並且處理啊,測試一下,果然視圖b中雖然沒有啟動taskB,但是依然彈出了toast,難道這種說法是對的嗎?
當然不對,主要有這么兩個問題。
第一個問題:handlerB能否接收到handlerA發送的消息?
不能,看看Message的創建方式
復制代碼
Message mes = new Message();
mHandler.sendMessage(mes);
//********************************
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
//********************************
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
//********************************
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
boolean sent = false;
MessageQueue queue = mQueue;
if (queue != null) {
msg.target = this;
sent = queue.enqueueMessage(msg, uptimeMillis);
}
else {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
}
return sent;
}
復制代碼
通過代碼我們可以看到Message會指定它的target為發送他的handler
另外一種方式:
復制代碼
Message mes2 = mHandler.obtainMessage();
mes2.sendToTarget();
//**********************************
public final Message obtainMessage()
{
return Message.obtain(this);
}
//***********************************
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}
復制代碼
這兩種創建方式都是一樣的。
再來看一下消息是怎麼分發的?
Looper會不斷的輪詢消息隊列,將消息發送給響應的handler進行處理
復制代碼
public static final void loop() {
Looper me = myLooper();
MessageQueue queue = me.mQueue;
while (true) {
Message msg = queue.next(); // might block
//if (!me.mRun) {
// break;
//}
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
if (me.mLogging!= null) me.mLogging.println(
">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what
);
msg.target.dispatchMessage(msg);
if (me.mLogging!= null) me.mLogging.println(
"<<<<< Finished to " + msg.target + " "
+ msg.callback);
msg.recycle();
}
}
}
//*****************************************************************************
復制代碼
看上面的紅色標注的代碼,他會調用這個Message的target的dispatchMessage(msg)分發,上面就說過了這個target就是發送這個消息的handler本身.
復制代碼
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
復制代碼
至此結束,handlerB根本不會得到handlerA的消息
第二個問題,既然handlerB不能獲得handlerA的消息,那麼又是如何彈的toast呢?
復制代碼
public static Toast makeText(Context context, CharSequence text, int ration) {
Toast result = new Toast(context);
LayoutInflater inflate = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
tv.setText(text);
result.mNextView = v;
result.mDuration = ration;
return result;
}
//*********************************************
public void show() {
if (mNextView == null) {
throw new RuntimeException("setView must have been called");
}
INotificationManager service = getService();
String pkg = mContext.getPackageName();
TN tn = mTN;
try {
service.enqueueToast(pkg, tn, mDuration);
} catch (RemoteException e) {
// Empty
}
}