在上面的一篇文章中讲到了线程池的执行流程,使用起来很简单。对于线程池的任务拒绝策略没有过多的介绍,本文主要介绍线程的四种拒绝策略。
RejectedExecutionHandler提供了多种方式来处理任务拒绝策略
通过观察源码可知:所有的拒绝策略他们都实现了RejectedExecutionHandler
1、直接丢弃(DiscardPolicy)
2、丢弃队列中最老的任务(DiscardOldestPolicy)。
3、抛异常(AbortPolicy)
4、将任务分给调用线程来执行(CallerRunsPolicy)。
运行一段代码,通过观察线程池里工作的线程池数,来查看线程池的工作状况
代码如下:
package juc.threadpool;
/**
* @Description
* @Author DJZ-WWS
* @Date 2019/5/22 10:12
*/
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(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);
}
}
简单的说明一下这里线程池的参数的配置:
corePoolSize我设置成了1,maxiPoolSize最大线程数设置成了 1,阻塞队列设置成了1,也就是说线程池最大的工作线程数不会超过两个,空闲时间设置成了0意味着线程一旦非核心工作线程停止了工作,并且没有任务的时候,那个线程就会立即关闭。线程池里面只剩下一个一个核心线程在工作。
结果简单分析:
可以看一下程序运行的结果:
我们配置的拒绝策略是 DiscardPolicy,源码如下:
我们可以看到这里面什么也没做,来了任务也不会将任务放到工作队列里。所以被执行的只有0,1线程,其他线程并没有在队列里,也不会被消费。
将上面代码改成另一个策略
这个策略意味着线程池任务过多的时候会报异常,运行结果如下:
Exception in thread "main" 2019-05-23 09:20:57 pool-1-thread-1begin run task :0
java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@5680a178 rejected from java.util.concurrent.ThreadPoolExecutor@5fdef03a[Running, pool size = 1, active threads = 1, queued tasks = 1, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
at juc.threadpool.ExecutorDemo.main(ExecutorDemo.java:27)
2019-05-23 09:20:58 pool-1-thread-1 finish run task :0
2019-05-23 09:20:58 pool-1-thread-1begin run task :1
2019-05-23 09:20:59 pool-1-thread-1 finish run task :1
工作线程还是0,1其他任务报异常了。
配置另一种任务CallerRunPolicy,
结果如下
这个拒绝策略的源码如下:
这个策略显然不想放弃执行任务。那么就用当前的Executor进行执行。不过,这样也有弊端,那就是阻塞当前Executor线程,造成该线程池无法调度任务。
配置另一种策略: DiscardOldestPolicy
结果如下
源码如下:
我们可以看出这个类通过线程池获取到这个线程池的队列,然后调用poll方法将队列头部的任务拉黑。因为队列中最先进去的任务在队列头部。所以这个实现策略是将最老的任务拒绝掉。由于9号线程是最新的一个线程,其他线程都是在9号之前,所有9号和0号被执行了。
以上就是jdk提供的四种拒绝策略。
学习参考:https://blog.csdn.net/u010412719/article/details/52132613
https://cloud.tencent.com/developer/news/305388