Java線程池有哪些狀態?如何切換?java
線程池的種類有哪些?數組
建立線程池須要哪些參數?含義?緩存
將任務添加到線程池的運行流程?ide
線程池怎麼重用線程?操作系統
線程池如何關閉?線程
來搞一下源碼:ThreadPoolExecutor.javacode
private static final int RUNNING = -1 << COUNT_BITS; private static final int SHUTDOWN = 0 << COUNT_BITS; private static final int STOP = 1 << COUNT_BITS; private static final int TIDYING = 2 << COUNT_BITS; private static final int TERMINATED = 3 << COUNT_BITS;
各類狀態的說明在源碼的註釋中都有,咱們來看一下:對象
The runState provides the main lifecycle control, taking on values:接口
RUNNING: Accept new tasks and process queued tasks SHUTDOWN: Don't accept new tasks, but process queued tasks STOP: Don't accept new tasks, don't process queued tasks,and interrupt in-progress tasks TIDYING: All tasks have terminated, workerCount is zero, the thread transitioning to state TIDYING will run the terminated() hook method TERMINATED: terminated() has completed
處於RUNNING狀態的線程池能夠接收新的任務,也可以處理阻塞隊列裏面的任務。隊列
SHUTDOWN:不能接受新的任務,能夠處理阻塞隊列裏面的任務。
STOP:不能接受新的任務,也不會處理阻塞隊列的任務。並且中斷正在處理的任務。
TIDYING:全部的任務都被終止,線程池中工做線程的數量爲0,將要調用terminated()方法。
TERMINATED: terminated()方法調用完畢,終止狀態。
狀態之間進行切換:
RUNNING -> SHUTDOWN On invocation of shutdown(), perhaps implicitly in finalize() (RUNNING or SHUTDOWN) -> STOP On invocation of shutdownNow() SHUTDOWN -> TIDYING When both queue and pool are empty STOP -> TIDYING When pool is empty TIDYING -> TERMINATED When the terminated() hook method has completed
RUNNING切換到SHUTDOWN狀態:調用shutdown()方法後切換到SHUTDOWN狀態。
RUNNING、SHUTDOWN切換到STOP狀態:調用shutdownNow()方法切換到STOP狀態
SHUTDOWN切換到TIDYING狀態:在阻塞隊列和線程池爲空的狀況下切換到TIDYING狀態。
STOP切換到TIDYING狀態:線程池爲空時切換到該狀態。
TIDYING切換到TERMINATED狀態: 調用terminated()方法切換到該狀態。
源碼中建立ThreadPoolExecutor的構造方法是這樣的:
public ThreadPoolExecutor int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
corePoolSize: 核心線程的數量,當一個任務條件到線程池中執行時,只要線程的數量達不到corePoolSize的數量,就會建立新的線程,直到線程數量達到corePoolSize的大小,再也不建立,將新的任務放到阻塞隊列中等待執行。 maximumPoolSize:線程池中容許建立最大線程數量,若是阻塞隊列已滿,建立的線程數量小於maximumPoolSize的話,會建立新的線程來處理阻塞隊列中的任務。 keepAliveTime:線程活動的保持時間,當線程數量大於corePoolSize時,核心線程以外的線程空閒時間,大於該時間將會被回收。 unit:keepAliveTime的單位。 workQueue: 阻塞隊列,當線程池中的線程數量爲corePoolSize時,新來的任務會放到該阻塞隊列中,有四種阻塞隊列: ArrayBlockingQueue:基於數組的阻塞隊列 LinkedBlockingQueue:基於鏈表的阻塞隊列 SychronusBlockingQueue:不存儲元素的阻塞隊列(沒見過) PriorityBlockingQueue:基於優先級的阻塞隊列 threadFactory:建立線程的工廠 handler: 當阻塞隊列滿了,沒有空閒線程的狀況下,線程中的數量已經達到了最大數目,必須採起一種策略來處理新來的任務。主要有四種策略:AbortPolicy(直接拋出異常)、CallerRunsPolicy(使用調用者的線程處理)、DiscardOldsPolciy(丟棄阻塞隊列中一個任務,處理當前任務)、Discard直接丟棄。
線程池對象有兩個方法用來執行線程: submit()和execute()方法。
區別是submit()方法能夠傳入一個實現Callable()接口的對象,在當前任務結束的時候能返回一個Future對象來獲取任務的返回值。 submit()方法仍是調用了execute()方法
public Future<?> submit(Runnable task) { if (task == null) throw new NullPointerException(); RunnableFuture<Void> ftask = newTaskFor(task, null); execute(ftask); return ftask; }
下面咱們重點來看一下execute()方法:
int c = ctl.get(); if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) return; c = ctl.get(); } if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); if (! isRunning(recheck) && remove(command)) reject(command); else if (workerCountOf(recheck) == 0) addWorker(null, false); } else if (!addWorker(command, false)) reject(command);
首先判斷當前線程的數量與核心線程數量比較,若是小於核心線程的數量,直接建立一個新的worker線程
若是當前線程數量大於corePoolSize的數量,嘗試添加任務到阻塞隊列裏面,而後第二次檢查線程的數量,若是線程池狀態不在RUNNING,直接移除,同時拒絕當前請求的任務,若是狀態爲RUNNING且線程的數量爲0,建立一個新的線程。
若是當前狀態不是RUNNING,則嘗試建立一個新的worker來處理任務,若是建立失敗,拒絕當前任務。
線程運行的四個階段: 1.poolSize<corePoolSize 當前線程數量小於核心線程數量,直接建立線程來處理
2.poolSize=corePoolSize,並且此時阻塞隊列沒有滿,將此任務添加到阻塞隊列裏面,若是此時存在工做線程(非核心線程),由工做線程來處理阻塞隊列中的任務,若是工做線程數量爲0,則會建立工做線程來處理 3. poolSize=corePoolSize 而且此時阻塞隊列滿了,直接建立新的工做線程來處理當前任務
4.poolSize=maxmumPoolSize:此時阻塞隊列也滿了,就會觸發拒絕機制,具體什麼策略由傳入的RejectExceptionHandler決定
線程池重用線程,主要是在執行worker線程時候去阻塞隊列裏面拿任務,不斷的去拿任務交給線程來執行,達到重用的目的,直到getTask爲null爲止。
final void runWorker(Worker w) { Thread wt = Thread.currentThread(); Runnable task = w.firstTask; w.firstTask = null; w.unlock(); // allow interrupts boolean completedAbruptly = true; try { while (task != null || (task = getTask()) != null) {
調用shutdown()和shutdownNow()方法
Java中經常使用線程池:
<p>newSingleThreadExecutor:建立一個單線程的線程池。這個線程池只有一個線程在工做,也就是至關於單線程串行執行全部任務。若是這個惟一的線程由於異常結束,那麼會有一個新的線程來替代它。此線程池保證全部任務的執行順序按照任務的提交順序執行。</p> <p>newFixedThreadPool:建立固定大小的線程池。每次提交一個任務就建立一個線程,直到線程達到線程池的最大大小。線程池的大小一旦達到最大值就會保持不變,若是某個線程由於執行異常而結束,那麼線程池會補充一個新線程。</p> <p>newCachedThreadPool:建立一個可緩存的線程池。若是線程池的大小超過了處理任務所須要的線程,那麼就會回收部分空閒(60秒不執行任務)的線程,當任務數增長時,此線程池又能夠智能的添加新線程來處理任務。此線程池不會對線程池大小作限制,線程池大小徹底依賴於操做系統(或者說JVM)可以建立的最大線程大小。</p> <p>newScheduledThreadPool:建立一個大小無限的線程池。此線程池支持定時以及週期性執行任務的需求。</p> <p>newSingleThreadExecutor:建立一個單線程的線程池。此線程池支持定時以及週期性執行任務的需求。</p>