接上文:java線程池的原理學習java
ThreadPoolExecutor
,線程池類,繼承自 AbstractExecutorService
segmentfault
public class ThreadPoolExecutor extends AbstractExecutorService
ThreadPoolExecutor
提供了四種構造方法實現(這裏只介紹一種):併發
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
有必要對每一個參數解釋一下:工具
corePoolSize
- 池中所保存的線程數,包括空閒線程。學習
maximumPoolSize
- 池中容許的最大線程數。this
keepAliveTime
- 當線程數大於核心時,此爲終止前多餘的空閒線程等待新任務的最長時間。線程
unit
- keepAliveTime
參數的時間單位。日誌
workQueue
- 執行前用於保持任務的隊列。此隊列僅保持由 execute
方法提交的 Runnable
任務。code
threadFactory
- 執行程序建立新線程時使用的工廠。orm
handler
- 因爲超出線程範圍和隊列容量而使執行被阻塞時所使用的處理程序。
爲了便於跨大量上下文使用,此類提供了不少可調整的參數和擴展鉤子 (hook)。jdk文檔中建議在一般狀況下,使用 Executors
提供的工廠方法配置,也就是提供好了的線程池。若非要手動配置,須要遵循如下規則:
ThreadPoolExecutor
將根據corePoolSize
和maximumPoolSize
設置的邊界自動調整池大小。當新任務在方法execute(java.lang.Runnable)
中提交時:
1.運行的線程少於corePoolSize
,則建立新線程來處理請求,即便其餘輔助線程是空閒的。
2:運行的線程多於corePoolSize
而少於maximumPoolSize
,則把任務放進隊列,由空閒線程從隊列中取任務,僅當隊列滿時才建立新線程。
3:若是設置的corePoolSize
和maximumPoolSize
相同,則建立了固定大小的線程池。
4:若是將maximumPoolSize
設置爲基本的無界值(如Integer.MAX_VALUE
),則容許池適應任意數量的併發任務。
還要注意如下兩點:
在大多數狀況下,核心和最大池大小僅基於構造器來設置,不過也可使用 setCorePoolSize(int)
和 setMaximumPoolSize(int)
進行動態更改。
當池中的線程數大於 corePoolSize
的時候,多餘的線程會等待 keepAliveTime
長的時間,若是無請求可處理就自行銷燬。
使用
ThreadFactory
建立新線程。若是沒有另外說明,則在同一個 ThreadGroup 中一概使用Executors.defaultThreadFactory()
建立線程,而且這些線程具備相同的NORM_PRIORITY
優先級和非守護進程狀態。經過提供不一樣的ThreadFactory
,能夠改變線程的名稱、線程組、優先級、守護進程狀態,等等。若是從newThread
返回 null 時ThreadFactory
未能建立線程,則執行程序將繼續運行,但不能執行任何任務。
ThreadFactory
是線程工廠,它是一個接口:
public interface ThreadFactory { Thread newThread(Runnable r); }
ThreadPoolExecutor
中的 threadFactory
是由 Executors
工具類提供的:
public static ThreadFactory defaultThreadFactory() { return new DefaultThreadFactory(); }
static class DefaultThreadFactory implements ThreadFactory { private static final AtomicInteger poolNumber = new AtomicInteger(1); private final ThreadGroup group; private final AtomicInteger threadNumber = new AtomicInteger(1); private final String namePrefix; DefaultThreadFactory() { SecurityManager s = System.getSecurityManager(); group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-"; } public Thread newThread(Runnable r) { ////建立的線程以「pool-N-thread-M」命名,N是該工廠的序號,M是線程號 Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); //設爲非後臺線程 if (t.isDaemon()) t.setDaemon(false); //優先級爲normal if (t.getPriority() != Thread.NORM_PRIORITY) t.setPriority(Thread.NORM_PRIORITY); return t; } }
DefaultThreadFactory
是一個靜態內部類
前面說到,當線程池中運行的線程等於或多於 corePoolSize
,則 Executor
始終首選將請求加入隊列,而不添加新的線程,將任務加入隊列有三種策略(具體參見jdk文檔)。
兩種狀況下,新提交的任務將會被拒絕:
當 Executor
已經關閉
Executor
將有限邊界用於最大線程和工做隊列容量,且已經飽和
被拒絕的任務, execute
方法都將調用其 RejectedExecutionHandler
的 RejectedExecutionHandler.rejectedExecution(java.lang.Runnable, java.util.concurrent.ThreadPoolExecutor)
方法。下面提供了四種預約義的處理程序策略:
在默認的
ThreadPoolExecutor.AbortPolicy
中,處理程序遭到拒絕將拋出運行時RejectedExecutionException
。
在ThreadPoolExecutor.CallerRunsPolicy
中,線程調用運行該任務的execute
自己。此策略提供簡單的反饋控制機制,可以減緩新任務的提交速度。
在ThreadPoolExecutor.DiscardPolicy
中,不能執行的任務將被刪除。
在ThreadPoolExecutor.DiscardOldestPolicy
中,若是執行程序還沒有關閉,則位於工做隊列頭部的任務將被刪除,而後重試執行程序(若是再次失敗,則重複此過程)。
定義和使用其餘種類的 RejectedExecutionHandler
類也是可能的,但這樣作須要很是當心,尤爲是當策略僅用於特定容量或排隊策略時。
此類提供兩個 protected
可重寫的 鉤子方法:
protected void beforeExecute(Thread t, Runnable r) { } protected void afterExecute(Runnable r, Throwable t) { }
這兩種方法分別在執行 每一個任務
以前和以後調用。它們可用於操縱執行環境;注意這裏是每一個任務,即每次運行新任務時都會執行一遍。例如,從新初始化 ThreadLocal
、蒐集統計信息或添加日誌條目。此外,還能夠重寫方法 terminated()
來執行 Executor
徹底終止後須要完成的全部特殊處理。
若是鉤子 (hook) 或回調方法拋出異常,則內部輔助線程將依次失敗並忽然終止。
jdk文檔中提供了一個能夠暫停和恢復的線程池例子:
class PausableThreadPoolExecutor extends ThreadPoolExecutor { private boolean isPaused; private ReentrantLock pauseLock = new ReentrantLock(); private Condition unpaused = pauseLock.newCondition(); public PausableThreadPoolExecutor(...) { super(...); } protected void beforeExecute(Thread t, Runnable r) { super.beforeExecute(t, r); pauseLock.lock(); try { while (isPaused) unpaused.await(); } catch(InterruptedException ie) { t.interrupt(); } finally { pauseLock.unlock(); } } public void pause() { pauseLock.lock(); try { isPaused = true; } finally { pauseLock.unlock(); } } public void resume() { pauseLock.lock(); try { isPaused = false; unpaused.signalAll(); } finally { pauseLock.unlock(); } } }