ThreadPoolExecutor的內部工作原理,整體思路總結爲5句話:
- 如果線程池大小poolSize小於corePoolSize,則創建新線程執行任務。
- 如果線程池大小poolSize大於corePoolSize,且等待隊列未滿,則進入等待隊列。
- 如果線程池大小poolSize大於corePoolSize且小於maximumPoolSize,且等待隊列已滿,則創建新線程執行任務。
- 如果線程池大小poolSize大於corePoolSize且大於maximumPoolSize,且等待隊列已滿,則調用拒絕策略來處理該任務。
- 線程池裏的每個線程執行完任務後不會立刻退出,而是會去檢查下等待隊列裏是否還有線程任務需要執行,如果在keepAliveTime裏等不到新的任務了,那麼線程就會退出。
ThreadPoolExecutor線程池中拒絕策略:
- AbortPolicy:爲java線程池默認的阻塞策略,不執行此任務,而且會直接拋出一個執行時異常,切記TreadPoolExecutor.execute需要try catch,否則程序會直接退出。
- DiscardPolicy:直接拋棄,任務不執行,空方法
- DiscardOldestPolicy:從隊列裏面拋棄head的一個任務,並再次execute 此任務(task)
- CallerRunsPolicy:在調用execute的線程裏面執行此command,會阻塞入口。
- 用戶自定義拒絕策略:實現RejectdExecutionHandler,並自己定義策略模式。
舉例說明:
使用直接丟棄任務本身的拒絕策略:DiscardPolicy
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ExecutorDemo {
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) {
int corePoolSize = 1;
int maximumPoolSize = 1;
BlockingQueue queue = new ArrayBlockingQueue<Runnable>(1);
ThreadPoolExecutor pool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
0, TimeUnit.SECONDS, queue ) ;
pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy ());
for(int i=0;i<10;i++){
final int index = i;
pool.submit(new Runnable(){
@Override
public void run() {
log(Thread.currentThread().getName()+"begin run task :"+index);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log(Thread.currentThread().getName()+" finish run task :"+index);
}
});
}
log("main thread before sleep!!!");
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log("before shutdown()");
pool.shutdown();
log("after shutdown(),pool.isTerminated=" + pool.isTerminated());
try {
pool.awaitTermination(1000L, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
log("now,pool.isTerminated=" + pool.isTerminated());
}
protected static void log(String string) {
System.out.println(sdf.format(new Date())+" "+string);
}
}
運行結果:
2016-08-04 22:29:21 main thread before sleep!!!
2016-08-04 22:29:21 pool-1-thread-1begin run task :0
2016-08-04 22:29:22 pool-1-thread-1 finish run task :0
2016-08-04 22:29:22 pool-1-thread-1begin run task :1
2016-08-04 22:29:23 pool-1-thread-1 finish run task :1
2016-08-04 22:29:25 before shutdown()
2016-08-04 22:29:25 after shutdown(),pool.isTerminated=false
2016-08-04 22:29:25 now,pool.isTerminated=true
從結果可以看出,只有task0和task1兩個任務被執行了。
爲什麼只有task0和task1兩個任務被執行了呢?
過程是這樣的:由於我們的任務隊列的容量爲1.當task0正在執行的時候,task1被提交到了隊列中但是還沒有執行,受隊列容量的限制,submit提交的task2~task9就都被直接拋棄了。因此就只有task0和task1被執行了。
使用丟棄任務隊列中比較久的任務的拒絕策略:DiscardOldestPolicy
如果將拒絕策略改爲:DiscardOldestPolicy(丟棄隊列中比較久的任務)
運行結果爲:
2016-08-04 22:31:58 pool-1-thread-1begin run task :0
2016-08-04 22:31:58 main thread before sleep!!!
2016-08-04 22:31:59 pool-1-thread-1 finish run task :0
2016-08-04 22:31:59 pool-1-thread-1begin run task :9
2016-08-04 22:32:00 pool-1-thread-1 finish run task :9
2016-08-04 22:32:02 before shutdown()
2016-08-04 22:32:02 after shutdown(),pool.isTerminated=false
2016-08-04 22:32:02 now,pool.isTerminated=true
從結果可以看出,只有task0和task9被執行了。
使用將任務將由調用者線程去執行的拒絕策略:CallerRunsPolicy
如果將拒絕策略改爲:CallerRunsPolicy(即不用線程池中的線程執行,而是交給調用方來執行)
運行結果爲:
2016-08-04 22:33:07 mainbegin run task :2
2016-08-04 22:33:07 pool-1-thread-1begin run task :0
2016-08-04 22:33:08 main finish run task :2
2016-08-04 22:33:08 mainbegin run task :3
2016-08-04 22:33:08 pool-1-thread-1 finish run task :0
2016-08-04 22:33:08 pool-1-thread-1begin run task :1
2016-08-04 22:33:09 pool-1-thread-1 finish run task :1
2016-08-04 22:33:09 main finish run task :3
2016-08-04 22:33:09 mainbegin run task :5
2016-08-04 22:33:09 pool-1-thread-1begin run task :4
2016-08-04 22:33:10 main finish run task :5
2016-08-04 22:33:10 mainbegin run task :7
2016-08-04 22:33:10 pool-1-thread-1 finish run task :4
2016-08-04 22:33:10 pool-1-thread-1begin run task :6
2016-08-04 22:33:11 main finish run task :7
2016-08-04 22:33:11 mainbegin run task :9
2016-08-04 22:33:11 pool-1-thread-1 finish run task :6
2016-08-04 22:33:11 pool-1-thread-1begin run task :8
2016-08-04 22:33:12 main finish run task :9
2016-08-04 22:33:12 main thread before sleep!!!
2016-08-04 22:33:12 pool-1-thread-1 finish run task :8
2016-08-04 22:33:16 before shutdown()
2016-08-04 22:33:16 after shutdown(),pool.isTerminated=false
2016-08-04 22:33:16 now,pool.isTerminated=true
從結果可以看出,沒有任務被拋棄,而是將由的任務分配到main線程中執行了。