通常狀況有下面三個緣由java
爲何會影響處理效率呢?
答:線程的建立須要開闢虛擬機棧、本地方法棧、程序計數器等線程私有的內存空間; 在線程銷燬時須要回收這些系統資源.頻繁地建立和銷燬線程會浪費大量的系統資源,增長併發編程風險.android
Android中線程池來自於Java,那麼研究Android線程池其實也能夠說是研究Java中的線程池git
在Java中,線程池的概念是Executor這個接口,具體實現爲ThreadPoolExecutor類,學習Java中的線程池,就能夠直接學習他了面試
對線程池的配置,就是對ThreadPoolExecutor構造函數的參數的配置,既然這些參數這麼重要,就來看看構造函數的各個參數吧數據庫
當全部的核心線程都在幹活時,新添加的任務會被添加到這個隊列中等待處理,若是隊列滿了,則新建非核心線程執行任務編程
經常使用的workQueue類型:緩存
SynchronousQueue:這個隊列接收到任務的時候,會直接提交給線程處理,而不保留它,若是全部線程都在工做怎麼辦?那就新建一個線程來處理這個任務!因此爲了保證不出現<線程數達到了maximumPoolSize而不能新建線程>的錯誤,使用這個類型隊列的時候,maximumPoolSize通常指定成Integer.MAX_VALUE,即無限大安全
LinkedBlockingQueue:這個隊列接收到任務的時候,若是當前線程數小於核心線程數,則新建線程(核心線程)處理任務;若是當前線程數等於核心線程數,則進入隊列等待。因爲這個隊列沒有最大值限制,即全部超過核心線程數的任務都將被添加到隊列中,這也就致使了maximumPoolSize的設定失效,由於總線程數永遠不會超過corePoolSizebash
ArrayBlockingQueue:能夠限定隊列的長度,接收到任務的時候,若是沒有達到corePoolSize的值,則新建線程(核心線程)執行任務,若是達到了,則入隊等候,若是隊列已滿,則新建線程(非核心線程)執行任務,又若是總線程數到了maximumPoolSize,而且隊列也滿了,則發生錯誤網絡
DelayQueue:隊列內元素必須實現Delayed接口,這就意味着你傳進去的任務必須先實現Delayed接口。這個隊列接收到任務時,首先先入隊,只有達到了指定的延時時間,纔會執行任務
maxPoolSize:最大線程數
keepAliveTime:線程空閒時間 當線程空閒時間達到keepAliveTime時,線程會退出,直到線程數量=corePoolSize 若是allowCoreThreadTimeout=true,則會直到線程數量=0 方法allowCoreThreadTimeout()能夠設置容許核心線程超時退出
TimeUnit :keepAliveTime的單位,TimeUnit是一個枚舉類型,其包括:
ThreadFactory 建立線程的方式,是一個接口,new他的時候須要實現他的Thread
rejectedExecutionHandler:任務拒絕處理器
ThreadPoolExecutor類有幾個內部實現類來處理這類狀況:
線程池按如下行爲執行任務
面試時可能常常會被問到的一個問題
通常狀況下有兩種方式能夠向線程池中提交任務
execute 調用execute()方法提交任務到線程池中進行執行,這個方法是沒有返回值的,比較經常使用,是ThreadPoolExecutor本身實現的方法;這種方式沒法判斷任務被線程池執行的狀況,好比是否成功;
submit 調用submit方法會有返回,完整的方法描述是:public Future<?> submit(Runnable task),其中泛型T是咱們回調的結果類型,若是咱們但願監放任務執行的結果,可使用submit方法提交任務,這個方法實際上是ThreadPoolExecutor的父類AbstractExecutorService的方法;
返回的future可經過get()獲取返回值,get()會阻塞當前線程直到任務完成,而使用get(long timeout,TimeUnit unit)方法則會阻塞當前線程一段時間後當即返回,這時候可能任務沒有執行完.
通常咱們可能不須要本身去根據實際須要進行參數設定對應的ThreadPoolExecutor來實現線程池,大部分狀況下,咱們使用java併發包中提供的Executors的類調用它的靜態方法獲取須要的線程池類型
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
複製代碼
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
複製代碼
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
複製代碼
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
複製代碼
可經過調用線程池的shutdown或shutdownNow方法來關閉線程池.
它們的原理是遍歷線程池中的工做線程,而後逐個調用線程的interrupt方法來中斷線程,因此沒法響應中斷的任務可能永遠沒法終止.
可是它們存在必定的區別
只要調用了這兩個關閉方法中的任意一個,isShutdown方法就會返回true.
當全部的任務都已關閉後,才表示線程池關閉成功,這時調用isTerminaed方法會返回true.
至於應該調用哪種方法,應該由提交到線程池的任務的特性決定,一般調用shutdown方法來關閉線程池,若任務不必定要執行完,則能夠調用shutdownNow方法.
要想合理地配置線程池,就必須首先分析任務特性,可從如下幾個角度來分析
使用不一樣規模的線程池進行處理
經過Runtime.getRuntime().availableProcessors()方法得到當前設備的CPU個數
優先級不一樣的任務可使用PriorityBlockingQueue處理.它可讓優先級高 的任務先執行. 若是一直有優先級高的任務提交到隊列裏,那麼優先級低的任務可能永遠不能執行,這一點須要特別注意
執行時間不一樣的任務能夠交給不一樣規模的線程池來處理,或者可使用優先級隊列,讓執行時間短的任務先執行.
建議使用有界隊列 有界隊列能增長系統的穩定性和預警能力,若是咱們設置成無界隊列,那麼線程池的隊列就會愈來愈多,有可能會撐滿內存,致使整個系統不可用,而不僅是後臺任務出現問題.固然這些對於java後臺的開發比較關鍵,對於android的開發相對比較少一些;
若是在系統中大量使用線程池,則有必要對線程池進行監控,方便在出現問題時,能夠根據線程池的使用情況快速定位問題.可經過線程池提供的參數進行監控,在監控線程池的時候可使用如下屬性:
taskCount:線程池須要執行的任務數量
completedTaskCount:線程池在運行過程當中已完成的任務數量,小於或等於taskCount。
largestPoolSize:線程池裏曾經建立過的最大線程數量.經過這個數據能夠知道線程池是否曾經滿過.如該數值等於線程池的最大大小,則表示線程池曾經滿過.
getPoolSize:線程池的線程數量.若是線程池不銷燬的話,線程池裏的線程不會自動銷燬,因此這個大小隻增不減.
getActiveCount:獲取活動的線程數.
複製代碼
經過擴展線程池進行監控.能夠經過繼承線程池來自定義線程池,重寫線程池的 beforeExecute、afterExecute和terminated方法,也能夠在任務執行前、執行後和線程池關閉前執行一些代碼來進行監控.例如,監控任務的平均執行時間、最大執行時間和最小執行時間等.
往期文章推薦
那些年你曾經工做過的奇葩公司能有多奇葩!
Kotlin擴展和對應的java代碼解析
git操做高級命令
瞭解一下計算機網絡層吧
計算機網絡基礎
帶你深刻了解運輸層
看看網絡安全的一些知識吧!
是時候瞭解一下應用層的知識了一塊兒來吧!
如何使用命令行卸載手機預知APP
長按二維碼關注公衆號,接收新的消息推送,值得期待喲!感謝您的支持!