如何處理線程池的併發?

【前言】咱們從事Android開發以來,都自始自終被灌輸着處理耗時的任務時要在非UI線程作。因而咱們有了各類處理併發的編程手段,不管是本身用new Thread(Runnable)新起工做線程(Worker thread),仍是利用Android提供的API(AsnyTask,CursorLaoder等)都是處理耗時任務的解決方案。可是在一個大型的應用程序中,若是咱們須要處理數量不少且頻繁的耗時任務時,若是仍是採用以前的手段,無疑會帶來不少不便;一來頻繁建立銷燬線程會形成資源(內存和Cpu)的浪費,二來代碼會顯得很凌亂。因而咱們提出了使用線程池來處理頻繁的耗時任務。java

在JDK1.5的類庫中,JAVA的開發者就給咱們提供了現成的線程池,java.util.concurrent包下就提供了線程池的api。編程

個人應用架構設計中,在處理併發的任務時,就要用到線程池,關於線程池,我把它想成是一個統一管理多線程任務的地方,具體來講,就是線程池接受多線程任務(實現Runnable接口),並經過建立線程池時配置好的一些參數來統一管理和執行這些任務,咱們在須要執行耗時任務的時候,只須要向線程池發送一個消息(但願執行的耗時操做)就能夠了,固然,咱們在發送請求的同時,能夠傳入一些回調的接口,這樣就能在耗時任務執行完畢以後獲得回調來更新UI。api

如何實現自定義的線程池?多線程

JDK提供的線程池當然能夠知足通常的開發需求,可是實現自定義的線程池仍是頗有必要的。架構

自定義線程池通常能夠繼承自類ThreadPoolExcutor,該類的構造方法裏面有幾個很重要的參數,這幾個參數決定着線程池的策略和處理能力。併發

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)線程

corePoolSize - 池中所保存的線程數,包括空閒線程。架構設計

maximumPoolSize - 池中容許的最大線程數。設計

keepAliveTime - 當線程數大於核心時,此爲終止前多餘的空閒線程等待新任務的最長時間。繼承

unit - keepAliveTime 參數的時間單位。

workQueue - 執行前用於保持任務的隊列。此隊列僅保持由 execute 方法提交的 Runnable 任務。

threadFactory - 執行程序建立新線程時使用的工廠。

handler - 因爲超出線程範圍和隊列容量而使執行被阻塞時所使用的處理程序。

這些參數既能夠在構造的時候傳入,也能夠在後期經過set方法來設定。

setMaximumPoolSize(int maximumPoolSize)

setRejectedExecutionHandler(RejectedExecutionHandler handler)

setKeepAliveTime(long time, TimeUnit unit)

setCorePoolSize(int corePoolSize)

線程池表達了一種邊界的概念,這個邊界是經過corePoolSize和maximumPoolSize來體現的,若是線程池接受的任務小於corePoolSize,則新提交的任務會被線程池經過新建線程來處理,若是大於corePoolSize而小於maximumPoolSize,則新傳入的任務會被排隊隊列容納,若是隊伍容納不下或者隊列是直接提交型隊列時,任務就會被提交給線程池,線程池會建立新線程來處理任務,若是大於maximumPoolSize,則新加入的任務會被拒絕,這裏要注意的時,被拒絕的意思並不等於被拋棄,只是會觸發線程池的RejectExecutionHandler,這裏咱們能夠經過設置自定義的handler來實現線程池的拒絕策略(好比用一個隊列來保存被拒絕的任務,之後還能夠從該隊列取出任務繼續執行)。

在自定義線程池的時候,選擇合適的排隊隊列顯得尤其重要,咱們通常有如下三種隊列可選

SynchronousQueue 直接提交隊列,該隊列不會保存任務,而是會把任務直接提交給線程池。

LinkedBlockingQueue 無界隊列,這隊列沒有極限,能夠無限加入任務保存,這樣maximumPoolSize就不起做用了,建立的線程數就永遠不會超過corePoolSize了。

ArrayBlockingQueue 有界隊列。

如何處理被拒絕任務?

1.定義被拒絕的策略

當 Executor 已經關閉,而且 Executor 將有限邊界用於最大線程和工做隊列容量,且已經飽和時,在方法 execute(java.lang.Runnable) 中提交的新任務將被拒絕。在以上兩種狀況下,execute 方法都將調用其RejectedExecutionHandler 的RejectedExecutionHandler.rejectedExecution(java.lang.Runnable, java.util.concurrent.ThreadPoolExecutor) 方法。

ThreadPoolExecutor.AbortPolicy

          用於被拒絕任務的處理程序,它將拋出 RejectedExecutionException.

ThreadPoolExecutor.DiscardOldestPolicy

          用於被拒絕任務的處理程序,它放棄最舊的未處理請求,而後重試 execute;若是執行程序已關閉,則會丟棄該任務。


ThreadPoolExecutor.DiscardPolicy

          用於被拒絕任務的處理程序,默認狀況下它將放棄被拒絕的任務。

2.自定義RejectedExecutionHandler

使用自定義RejectedExecutionHandler來定義線程池的拒絕策略,好比用一個隊列來保存被拒絕的任務以便之後繼續執行或者直接丟棄。

更多靈活的處理

ThreadPoolExcutor提供了一些可被覆寫的hook方法

afterExecute(Runnable r,Throwable t)

          基於完成執行給定 Runnable 所調用的方法。

beforeExecute(Thread t,Runnable r)

          在執行給定線程中的給定 Runnable 以前調用的方法。

這兩個方法在每一個任務被線程池執行先後都被會回調,因此咱們能夠覆寫這兩個方法來自定義須要的功能。

相關文章
相關標籤/搜索