本篇单独讲解线程池的拒绝策略,介绍了当线程池任务满了之后,线程池会以什么样的方式来响应添加进来的任务
目录
一:理解线程池拒绝策略的触发情况+代码理解
二:线程池的四种常见的拒绝策略
1.ThreadPoolExecutor.AbortPolicy
2.ThreadPoolExecutor.CallerRunsPolicy
3.ThreadPoolExecutor.DiscardOldestPolicy
4.ThreadPoolExecutor.DiscardPolicy
一:理解线程池拒绝策略的触发情况+代码理解
当线程池容纳不了任务的时候,则会触发线程池的拒绝策略
线程池能同时容纳的任务数量:最大线程数+任务队列能存储的任务数
看代码理解:
public static void main(String[] args) {
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(1);
ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 2, 10, TimeUnit.SECONDS, queue, new ThreadPoolExecutor.AbortPolicy()) ;
//此时线程池只有一个核心线程,且队列也只能存储一个任务
//当我们添加第三个任务时,没地放了,此时会根据最大线程数来新建临时线程执行这个线程,此时创建了一个临时线程执行任务3
//如果我们再添加任务四:此时最大线程数和存储的队列加起来只能拿到三个任务,那么此时任务四就没地方存储了
//此时就会触发线程池的拒绝策略
for (int i = 1; i <= 4; i++) {
int num = i;
pool.submit(new Runnable() {
@Override
public void run() {
while(true){
System.out.println("正在执行线程"+num);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
}
}
此处创建了一个线程池pool,核心线程数为1,最大线程数为2,任务队列的长度为1
设置线程池的拒绝策略为ThreadPoolExecutor.AbortPolicy
运行结果:
详解:
1.当添加第1个任务进线程池执行的时候,此时线程池直接创建核心线程来执行任务
2.当添加第2个任务进线程池执行的时候,此时还有存储空间,那么第2个任务进去任务队列,等待核心线程执行完毕再弹出任务2进行执行
3.当添加第3个任务进线程池执行的时候,此时核心线程在执行任务1,而任务队列又被任务2给占满了,那么任务3没地方去了,但此时线程数量还没到线程池的最大线程数,这个时候会自动创建临时线程来执行任务3,把任务3给拿走,避免任务3流失
4.当添加第4个任务进线程池执行的时候:此时核心线程数被占用,任务队列和最大线程数都满了,那么此时任务4彻底没地方去了,线程池只能被迫采用拒绝策略来处理当前问题
二:线程池的四种常见的拒绝策略
首先打开官方文档
可查看到以下四种策略:
1.ThreadPoolExecutor.AbortPolicy
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(1);
ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 2, 10, TimeUnit.SECONDS, queue, new ThreadPoolExecutor.AbortPolicy()) ;
这种拒绝策略表示:当线程池最大的任务容量已满时,编译器直接抛出异常
上诉代码就是这种情况
通俗理解:
就好像在说,这个问题编译器傻眼了,处理不了,编译器给你一个异常你自己看着办。
2.ThreadPoolExecutor.CallerRunsPolicy
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(1);
ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 2, 10, TimeUnit.SECONDS, queue, new ThreadPoolExecutor.CallerRunsPolicy()) ;
这种拒绝策略表示:当线程池最大的任务容量已满时,线程池不处理该任务,让添加该任务的线程自己执行
例如前面代码案例:如果设置为这个拒绝策略,那么是main线程添加任务4进线程池被拒绝了,此时main线程自己执行任务4
通俗理解:
我把一个任务交给你执行,你自己也没空闲时间,所以你让我自己去做这个任务
3.ThreadPoolExecutor.DiscardOldestPolicy
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(1);
ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 2, 10, TimeUnit.SECONDS, queue, new ThreadPoolExecutor.DiscardOldestPolicy) ;
把前面例子改变一下:设置拒绝策略为DiscardOldestPolicy
并且把代码的死循环改为执行3秒
public static void main(String[] args) throws InterruptedException {
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(1);
ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 2, 10, TimeUnit.SECONDS, queue, new ThreadPoolExecutor.DiscardOldestPolicy()) ;
//此时线程池只有一个核心线程,且队列也只能存储一个任务
//当我们添加第三个任务时,没地放了,此时会根据最大线程数来新建临时线程执行这个线程,此时创建了一个临时线程执行任务3
//如果我们再添加任务四:此时最大线程数和存储的队列加起来只能拿到三个任务,那么此时任务四就没地方存储了
//此时就会触发线程池的拒绝策略
for (int i = 1; i <= 4; i++) {
int num = i;
pool.submit(new Runnable() {
@Override
public void run() {
boolean start = true;
long time = System.currentTimeMillis();
while(start){
System.out.println("正在执行线程"+num);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(System.currentTimeMillis() - time >= 3000){
start = false;
}
}
}
});
}
}
此时代码结果:
这种拒绝策略表示:当线程池最大的任务容量已满时,线程池拒绝(删除掉)最老的任务,再把当前任务添加进线池
例如前面代码案例:如果设置为这个拒绝策略,那么线程池就把任务队列中最早添加的(最老的)的任务2给删除,再把任务4添加进线程池
通俗理解:
我把任务交你执行,你手底下也有很多任务也没时间做,此时你放下把后续任务中的最早添加的任务给剔除,再把新任务加入后续执行的任务中
4.ThreadPoolExecutor.DiscardPolicy
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(1);
ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 2, 10, TimeUnit.SECONDS, queue, new ThreadPoolExecutor.DiscardPolicy ) ;
这种拒绝策略表示:当线程池足底啊的任务容量已满时,线程池拒绝最新添加的任务
例如前面代码案例:如果设置为这个拒绝策略,那么线程池就直接拒绝任务4,不接收任务4
通俗理解:
我把任务交你执行,你手底下也有很多任务也没时间做,此时你直接拒绝这个任务,不做这个任务,那么这个任务就流失了