Java线程池拒绝策略经常在Java面试被问到,具体有哪些策略?作用等,下面我详解线程池拒绝策略@mikechen
线程池拒绝策略作用
当线程池的线程数达到最大线程数时,需要执行拒绝策略,是在线程池无法接受新的任务时所采取的处理方式,这就是线程池的拒绝策略。
线程池拒绝策略种类
常见的拒绝策略有以下4种,如下图所示:
1.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()); } }
很简单粗暴,直接抛出个RejectedExecutionException异常,也不执行这个任务了。
适用场景:该策略适合希望立即知道任务被拒绝的情况,并由调用方自行处理异常。
优点:明确任务被拒绝,避免吞掉任务不处理的情况。
缺点:调用方需要处理异常,否则会影响程序的稳定性。
2.CallerRunsPolicy
CallerRunsPolicy在任务被拒绝添加后,会用调用execute函数的上层线程去执行被拒绝的任务。
public static class CallerRunsPolicy implements RejectedExecutionHandler { public CallerRunsPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { r.run(); } } }
在调用execute方法的线程中直接运行被拒绝的任务,可以减缓新任务的流量。
适用场景:适合希望任务总能被执行,并且能减缓提交任务的速度的场景;
优点:保证了任务不会被丢弃,且能有效减缓任务提交的速度;
缺点:调用线程的响应时间可能会增加,因为它要执行任务。
3.DiscardOldestPolicy
抛弃任务队列中等待时间最长的任务,并尝试重新提交当前被拒绝的任务。
public static class DiscardOldestPolicy implements RejectedExecutionHandler { public DiscardOldestPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { e.getQueue().poll(); e.execute(r); } } }
DiscardOldestPolicy策略的作用是,当任务呗拒绝添加时,会抛弃任务队列中最旧的任务也就是最先加入队列的,再把这个新任务添加进去。
适用场景:适合某些任务优先级较高的场景(比如:新的任务更重要),不介意丢弃最早的任务,来腾出队列空间处理最新的任务。
优点:能够让新的任务有机会得到执行。
缺点:可能会丢弃较早的任务,导致系统状态不一致。
4.DiscardPolicy
直接抛弃被拒绝的任务,不做任何处理。
public static class DiscardPolicy implements RejectedExecutionHandler { public DiscardPolicy() { } public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { } }
采用这个拒绝策略,会让被线程池拒绝的任务直接抛弃,不会抛弃也不会执行。
适用场景:适合任务不重要的场景,允许丢弃部分任务来保证系统稳定性。
优点:简单有效,避免了任务堆积、和线程阻塞。
缺点:任务被悄悄丢弃,调用方不会感知,可能会导致数据丢失、或状态不一致问题。
陈睿mikechen
10年+大厂架构经验,资深技术专家,就职于阿里巴巴、淘宝、百度等一线互联网大厂。
关注「mikechen」公众号,获取更多技术干货!
后台回复【面试】即可获取《史上最全阿里Java面试题总结》,后台回复【架构】,即可获取《阿里架构师进阶专题全部合集》