线程池–拒绝策略RejectedExecutionHandler
当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize
,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
当Executor已经关闭(即执行了executorService.shutdown()
方法后),并且Executor
将有限边界用于最大线程和工作队列容量,且已经饱和时,在方法execute()
中提交的新任务将被拒绝.
在以上述情况下,execute
方法将调用其 RejectedExecutionHandler
的RejectedExecutionHandler.rejectedExecution(java.lang.Runnable, java.util.concurrent.ThreadPoolExecutor)
方法。
线程池默认会采用的是defaultHandler
策略。首先看defaultHandler
的定义:
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy(); // 使用默认的拒绝策略
//丢弃任务并抛出RejectedExecutionException异常。
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
// 抛出异常
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString());
}
}
接下来 是一个线程类 ,将实现四种拒绝策略的demo
package com.thread.exe.entity;
/**
* Created by Andrew Feng on 2020/3/26
*/
public class RunnableTest implements Runnable {
volatile int i;
public RunnableTest(int i) {
this.i = i;
}
@Override
public void run() {
System.out.println("start");
System.out.println("task_" +i + " is running");
System.out.println("end");
}
}
看一下其他拒绝策略的具体实现。
1.DiscardPolicy
示例(也是丢弃任务,但是不抛出异常。)
public class DiscardPolicyDemo {
/**
* 核心线程数
*/
private static final int THREDSSIZE = 1;
/**
* 最大线程数
*/
private static final int CAPACITY = 1;
public static void main(String[] args) {
//创建线程池 策略为丢弃
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(THREDSSIZE,THREDSSIZE,0, TimeUnit.SECONDS,new ArrayBlockingQueue<>(CAPACITY),new ThreadPoolExecutor.DiscardPolicy());
for (int i = 0; i < 10; i++){
RunnableTest runnableTest = new RunnableTest(i);
threadPoolExecutor.execute(runnableTest);
}
threadPoolExecutor.shutdown();
}
}
线程池pool
的”最大池大小”和”核心池大小”都为1(THREDSSIZE
),这意味着”线程池能同时运行的任务数量最大只能是1”。
线程池pool
的阻塞队列是[ArrayBlockingQueue
],ArrayBlockingQueue
是一个有界的阻塞队列,ArrayBlockingQueue
的容量为1。这也意味着线程池的阻塞队列只能有一个线程池阻塞等待。
根据”“中分析的execute()
代码可知:线程池中共运行了2个任务。第1个任务直接放到Worker
中,通过线程去执行;第2个任务放到阻塞队列中等待。其他的任务都被丢弃了!
2.DiscardOldestPolicy
示例(丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程))
public class DiscardOldestPolicyDemo {
private static final int THREAD_SIZE = 1;
private static final int CAPACITY = 1;
public static void main(String[] args) {
long keepAliveTime = 1;
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(CAPACITY);
RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardOldestPolicy();
ThreadPoolExecutor pool = new ThreadPoolExecutor(
THREAD_SIZE,THREAD_SIZE,keepAliveTime,unit,workQueue,handler);
for (int i = 0; i < 10; i++) {
RunnableTest runnableTest = new RunnableTest(CAPACITY);
pool.execute(runnableTest);
}
pool.shutdown();
}
}
运行结果:
start
task_1 is running
end
start
task_1 is running
end
将”线程池的拒绝策略”由DiscardPolicy
修改为DiscardOldestPolicy
之后,当有任务添加到线程池被拒绝时,线程池会丢弃阻塞队列中末尾的任务,然后将被拒绝的任务添加到末尾。
3.AbortPolicy
示例(丢弃任务并抛出RejectedExecutionException
异常。)
public class AbortPolicyDemo {
public static final int THREADS_SIZE = 1;
public static final int CAPACITY = 1;
public static void main(String[] args) throws Exception {
long keepAliveTime = 1;
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(CAPACITY);
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
ThreadPoolExecutor pool = new ThreadPoolExecutor(
THREADS_SIZE,THREADS_SIZE,keepAliveTime,unit,workQueue,handler);
try {
for (int i = 0; i < 10;i++ ){
RunnableTest runnableTest = new RunnableTest(i);
pool.execute(runnableTest);
}
}catch(Exception e){
System.out.println("异常为:"+e.toString());
}finally {
pool.shutdown();
}
}
}
(某一次运行结果)
异常为:java.util.concurrent.RejectedExecutionException: Task com.thread.exe.entity.RunnableTest@1540e19d rejected from java.util.concurrent.ThreadPoolExecutor@677327b6[Running, pool size = 1, active threads = 1, queued tasks = 1, completed tasks = 0]
start
task_0 is running
end
start
task_1 is running
end
将”线程池的拒绝策略”由DiscardPolicy
修改为AbortPolicy
之后,当有任务添加到线程池被拒绝时,会抛出RejectedExecutionException
。
4.CallerRunsPolicy
示例(由调用线程处理该任务)
public class CallerRunsPolicy {
private static final int THREAD_SIZE = 1;
private static final int CAPACITY = 1;
public static void main(String[] args) {
long keepAliveTime = 0;
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<Runnable> (CAPACITY);
RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();
//创建线程池 拒绝策略为 CallerRunsPolicy
ThreadPoolExecutor pool = new ThreadPoolExecutor
(THREAD_SIZE,THREAD_SIZE,keepAliveTime,unit,workQueue,handler);
//新建10个任务,并添加到线程池中
for (int i=0;i<10;i++) {
RunnableTest myrun = new RunnableTest(i);
pool.execute(myrun);
}
//关闭线程池
pool.shutdown();
}
}
某一次运行结果
start
task_2 is running
end
start
task_3 is running
end
start
task_4 is running
end
start
task_5 is running
end
start
task_6 is running
end
start
task_7 is running
end
start
task_8 is running
end
start
task_9 is running
end
start
task_0 is running
end
start
task_1 is running
end
将”线程池的拒绝策略”由DiscardPolicy
修改为CallerRunsPolicy
之后,当有任务添加到线程池被拒绝时,线程池会将被拒绝的任务添加到”线程池正在运行的线程”中取运行。