為什么要使用執行緒池
- 降低系統資源消耗,通過重用已存在的執行緒,降低執行緒創建和銷毀造成的消耗;
- 提高系統回應速度,當有任務到達時,通過復用已存在的執行緒,無需等待新執行緒的創建便能立即執行;
- 方便執行緒并發數的管控,因為執行緒若是無限制的創建,可能會導致記憶體占用過多而產生OOM,并且會造成cpu過度切換(cpu切換執行緒是有時間成本的(需要保持當前執行執行緒的現場,并恢復要執行執行緒的現場)),
ThreadPoolExecutor

corePoolSize :執行緒池的核心池大小,在創建執行緒池之后,執行緒池默認沒有任何執行緒,當有任務過來的時候才會去創建創建執行緒執行任務,換個說法,執行緒池創建之后,執行緒池中的執行緒數為0,當任務過來就會創建一個執行緒去執行,直到執行緒數達到corePoolSize ,如果還有任務加入則會將任務加入到BlockingQueue中,注意:當向執行緒池提交一個任務時,若執行緒池已創建的執行緒數小于corePoolSize,即便此時存在空閑執行緒,也會通過創建一個新執行緒來執行該任務,
- prestartCoreThread(); 可以提前創建一個執行緒,
- prestartAllCoreThreads():可以提前創建所有的核心執行緒,

maximumPoolSize:執行緒池所允許的最大執行緒個數,當佇列滿了,且已創建的執行緒數小于maximumPoolSize,則執行緒池會創建新的執行緒來執行任務,另外,對于無界佇列,可忽略該引數,按照上面代碼中的引數來說明:當添加的任務數大于 corePoolSize+blockQueue.size時,該引數將會生效;即當我向執行緒池提交22個任務,此時executor.getPoolSize()=6,
keepAliveTime:執行緒存活保持時間;當執行緒池中執行緒數大于核心執行緒數時,執行緒的空閑時間如果超過執行緒存活時間,那么這個執行緒就會被銷毀,直到執行緒池中的執行緒數小于等于核心執行緒數,
TimeUnit:keepAliveTime的單位;
workQueue:任務佇列,用于存盤等待執行任務的阻塞佇列,
threadFactory:執行緒工廠,用于創建新執行緒,threadFactory創建的執行緒也是采用new Thread()方式,threadFactory創建的執行緒名都具有統一的風格:pool-m-thread-n(m為執行緒池的編號,n為執行緒池內的執行緒編號),
handler:執行緒飽和策略,當執行緒池和佇列都滿了,再加入執行緒會執行此策略,
- AbortPolicy:丟棄任務并拋出RejectedExecutionException
- CallerRunsPolicy:只要執行緒池未關閉,該策略直接在呼叫者執行緒中,運行當前被丟棄的任務,顯然這樣做不會真的丟棄任務,但是,任務提交執行緒的性能極有可能會急劇下降,
- DiscardOldestPolicy:丟棄佇列中最老的一個請求,也就是即將被執行的一個任務,并嘗試再次提交當前任務,
- DiscardPolicy:丟棄任務,不做任何處理,
執行緒池的關閉
ThreadPoolExecutor提供了兩個方法,用于執行緒池的關閉,分別是shutdown()和shutdownNow(),其中:- shutdown():不會立即終止執行緒池,而是要等所有任務快取佇列中的任務都執行完后才終止,但再也不會接受新的任務
- shutdownNow():立即終止執行緒池,并嘗試打斷正在執行的任務,并且清空任務快取佇列,回傳尚未執行的任務
常見的四種執行緒池
1、newFixedThreadPool :固定大小的執行緒池,可以指定執行緒池的大小,該執行緒池corePoolSize和maximumPoolSize相等,阻塞佇列使用的是LinkedBlockingQueue,大小為整數最大值,該執行緒池中的執行緒數量始終不變,當有新任務提交時,執行緒池中有空閑執行緒則會立即執行,如果沒有,則會暫存到阻塞佇列,對于固定大小的執行緒池,不存在執行緒數量的變化,同時使用無界的LinkedBlockingQueue來存放執行的任務,當任務提交十分頻繁的時候,LinkedBlockingQueue迅速增大,存在著耗盡系統資源的問題,而且在執行緒池空閑時,即執行緒池中沒有可運行任務時,它也不會釋放作業執行緒,還會占用一定的系統資源,需要shutdown,
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
2、newSingleThreadExecutor :單個執行緒執行緒池,只有一個執行緒的執行緒池,阻塞佇列使用的是LinkedBlockingQueue,若有多余的任務提交到執行緒池中,則會被暫存到阻塞佇列,待空閑時再去執行,按照先入先出的順序執行任務,
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
3、newCachedThreadPool:快取執行緒池,快取的執行緒默認存活60秒,執行緒的核心池corePoolSize大小為0,核心池最大為Integer.MAX_VALUE,阻塞佇列使用的是SynchronousQueue,是一個直接提交的阻塞佇列, 他總會迫使執行緒池增加新的執行緒去執行新的任務,在沒有任務執行時,當執行緒的空閑時間超過keepAliveTime(60秒),則作業執行緒將會終止被回收,當提交新任務時,如果沒有空閑執行緒,則創建新執行緒執行任務,會導致一定的系統開銷,如果同時又大量任務被提交,而且任務執行的時間不是特別快,那么執行緒池便會新增出等量的執行緒池處理任務,這很可能會很快耗盡系統的資源,
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
4、newScheduledThreadPool:定時執行緒池,該執行緒池可用于周期性地去執行任務,通常用于周期性的同步資料,
scheduleAtFixedRate:是以固定的頻率去執行任務,周期是指每次執行任務成功執行之間的間隔,
schedultWithFixedDelay:是以固定的延時去執行任務,延時是指上一次執行成功之后和下一次開始執行的之前的時間,
執行緒池的正確使用:
阿里編碼規范里面說:執行緒池不允許使用Executors去創建,而是通過ThreadPoolExecutor的方式,這樣的處理方式讓寫的同學更加明確執行緒池的運行規則,規避資源耗盡的風險,
Executors各個方法的弊端:
- newFixedThreadPool和newSingleThreadExecutor:主要問題是堆積的請求處理佇列可能會耗費非常大的記憶體,甚至OOM,
- newCachedThreadPool和newScheduledThreadPool:主要問題是執行緒數最大數是Integer.MAX_VALUE,可能會創建數量非常多的執行緒,甚至OOM,
一個執行緒池中的執行緒例外了,那么執行緒池會怎么處理這個執行緒?
- 執行緒池中一個執行緒例外,不會影響其他執行緒任務
- 執行緒池例外的執行緒,不會重新放入執行緒池,會洗掉掉重新創建
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/276570.html
標籤:其他
