Java執行緒池(ThreadPoolFactory)構造引數總結
今天看到關於執行緒池的一篇帖子,是關於面試時問到ThreadPoolFactory構造器時的一些問題,之前博主也學習過一些關於ThreadPoolFactory構造器的問題,但是一直沒有總結過,既然今天有時間,那麼就總結一下,避免有些同學走彎路(有些工作多年的老鳥也不一定能準確的說明coreSize,MaxSize,workQueueSize的關係),話不多說上乾貨。
下面來直接看ThreadPoolFactory最詳細的構造器結構
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<0
maximumPoolSize<=0
maximumPoolSize<corePoolSize
keepAliveTime<0
從構造引數的入口可以看出主要的幾個引數一共有如下幾個
int corePoolSize
int maximumPoolSize
long keepAliveTime
TimeUnit unit
BlockingQueue workQueue
ThreadFactory threadFactory
RejectedExecutionHandler handler
而一般最讓人迷糊的3個引數就是corePoolSize,maxmumPoolSize,workQueueSize
下面就針對這幾個引數做下總結:
當執行緒池初始化時,PoolSize為0,當有任務向執行緒中提交時,執行緒池開始建立執行緒,用來執行業務。
一、corePoolSize、maxmumPoolSize、workQueueSize引數關係總結
JobCount代表向執行緒池中新增的任務數
1.JobCount <=corePoolSize<maxmumPoolSize<workQueueSize
(當JobCount小於等於corePoolSize時,workQueueSize與maxmumPoolSize無效,執行緒池會建立與JobCount個數一致的執行緒來執行任務)
2.corePoolSize <JobCount <=workQueueSize <maxmumPoolSize
(當JobCount大於corePoolSize並且小於等於workQueueSize時maxmumPoolSize無效,超出corePoolSize的任務,會在阻塞對列中排隊,當corePoolSize中有執行緒閒置的時候,從workQueue中取出執行)
3.corePoolSize<workQueueSize<JobCount<maxmumPoolSize
(當JobCount大於corePoolSize時並且workQueue中也放滿,但JobCount小於maxmumPoolSize時,那麼執行緒池會建立執行緒來處理超出workQueueSize部分的任務)
4.corePoolSize<workQueueSize+maxmumPoolSize<JobCount
(當JobCount大於corePoolSize並且大於workQueueSize和maxmumPoolSize之和。那麼執行緒池將會呼叫handler.rejectedExecution方法)
二、RejectedExecutionHandler引數總結
上面說了corePoolSize,maxmumPoolSize,workQueueSize 3個引數的用法了,那麼接下來說一下RejectedExecutionHandler這個引數的意思。
從字面上理解為拒絕處理者,可以理解為當任務數大於maxmumPoolSize後的一個回撥方法,RejectedExecutionHandler本身是一個介面,jdk本身對他有4個實現類
CallerRunsPolicy //執行緒呼叫執行該任務的 execute 本身。此策略提供簡單的反饋控制機制,能夠減緩新任務的提交速度
AbortPolicy(如不指定handler則預設這個) //處理程式遭到拒絕將丟擲執行時RejectedExecutionException;
DiscardPolicy //不能執行的任務將被刪除
DiscardOldestPolicy //如果執行程式尚未關閉,則位於工作佇列頭部的任務將被刪除,然後重試執行程式(如果再次失敗,則重複此過程)
三、KeepAliveTime、TimeUnit引數總結
TimeUnit引數很好理解,是一個用來當做單位的列舉。主要還是說說keepAliveTime這個引數,網上很多對這個引數都是幾句話帶過,說是用來控制執行緒回收時間的引數,確實他的作用就是這樣,但是看下面例子
public class Test {
public static void main(String[] args) {
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(1);
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(1, 5, 5, TimeUnit.SECONDS, queue);
System.out.println("pool初始化時PoolSize大小:"+poolExecutor.getPoolSize());
System.out.println("pool初始化時active大小:"+poolExecutor.getActiveCount());
poolExecutor.execute(new Test().new Task("1"));
poolExecutor.execute(new Test().new Task("2"));
poolExecutor.execute(new Test().new Task("3"));
poolExecutor.execute(new Test().new Task("4"));
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("pool現在PoolSize大小:"+poolExecutor.getPoolSize());
System.out.println("pool現在active大小:"+poolExecutor.getActiveCount());
// poolExecutor.shutdown();
}
class Task implements Runnable {
String name;
public Task(String name) {
this.name = name;
}
@Override
public void run() {
int i=0;
while (true) {
i++;
if (name.equals("4")) {
if (i==5) {
break;
}
}
if (name.equals("2")) {
if (i==2) {
break;
}
}
System.out.println(name);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
例子看上去不是太明白,但是keepAliveTime本身就不是很容易觸發
1.keepAliveTime和maximumPoolSize及BlockingQueue的型別均有關係。如果BlockingQueue是無界的,那麼永遠不會觸發maximumPoolSize,自然keepAliveTime也就沒有了意義。
2.反之,如果核心數較小,有界BlockingQueue數值又較小,同時keepAliveTime又設的很小,如果任務頻繁,那麼系統就會頻繁的申請回收執行緒。
上面的例子總結起來就是
1.假設corePoolSize=1,QueueSize=1,MaxmumPoolSize=5
2.當初始化執行緒池的時候CorePoolSize為0,假設這個時候來了4個任務為t1、t2、t3、t4
3.那麼執行緒池會建立一個執行緒用來執行t1
4.接下來發現coreSize不足,那麼將t2放到Queue中
5.又發現QueuePool的大小不夠了,判斷MaxSize大小沒有超出,那麼接下來申請執行緒執行t3和t4
6.,執行了一會發現t4執行完畢了,(PS:注意了)這個時候不管keepaliveTime不管設定的多小,也不會回收的,因為QueuPool中還有一個t2沒有執行,那麼現在就會拿剛才用來執行t4的執行緒執行t2,執行的時候發現t2執行的很快,那麼現線上程池的狀態就是一共有3個執行緒,兩個屬於active的,那麼這個時候就靠keepAliveTime引數判斷回收時間了。
可以將TimeUnit的單位修改為NANOSECONDS比較兩次的輸出,就會明白了