在没有分析线程池原理以前先来分析下为何有任务拒绝的状况发生。html
这里先假设一个前提:线程池有一个任务队列,用于缓存全部待处理的任务,正在处理的任务将从任务队列中移除。所以在任务队列长度有限的状况下就会出现新任务的拒绝处理问题,须要有一种策略来处理应该加入任务队列却由于队列已满没法加入的状况。另外在线程池关闭的时候也须要对任务加入队列操做进行额外的协调处理。java
RejectedExecutionHandler提供了四种方式来处理任务拒绝策略缓存
一、直接丢弃(DiscardPolicy)ide
二、丢弃队列中最老的任务(DiscardOldestPolicy)。.net
三、抛异常(AbortPolicy)线程
四、将任务分给调用线程来执行(CallerRunsPolicy)。orm
这四种策略是独立无关的,是对任务拒绝处理的四中表现形式。最简单的方式就是直接丢弃任务。可是却有两种方式,究竟是该丢弃哪个任务,好比能够丢弃当前将要加入队列的任务自己(DiscardPolicy)或者丢弃任务队列中最旧任务(DiscardOldestPolicy)。丢弃最旧任务也不是简单的丢弃最旧的任务,而是有一些额外的处理。除了丢弃任务还能够直接抛出一个异常(RejectedExecutionException),这是比较简单的方式。抛出异常的方式(AbortPolicy)尽管实现方式比较简单,可是因为抛出一个RuntimeException,所以会中断调用者的处理过程。除了抛出异常之外还能够不进入线程池执行,在这种方式(CallerRunsPolicy)中任务将有调用者线程去执行。htm
下面来看下这几种拒绝策略的例子。blog
使用直接丢弃任务自己的拒绝策略:DiscardPolicy
队列
[Java] 纯文本查看 复制代码
?
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
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线程中执行了。
小结
关于线程池的任务拒绝策略,咱们要理解并记住,有以下的四种:
一、直接丢弃(DiscardPolicy)
二、丢弃队列中最老的任务(DiscardOldestPolicy)。
三、抛异常
四、将任务分给调用线程来执行。