在使用线程池 ThreadPoolExecutor 创建线程池时,我们都会使用到这样的一个参数 RejectedExecutionHandler handler
,这就是线程池拒绝策略的抽象接口,在 Java 中提供了4种策略供我们使用,分别是AbortPlicy、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy
,下面就会展开分析这4种策略的区别。
拒绝策略的主要作用就是,当线程池达到最大的线程数或者线程队列满了的情况下,需要线程池对后续提交的线程任务做出的处理方式。
1 | /** |
拒绝策略的核心是: rejectedExecution
方法,我们要想实现自定义的拒绝逻辑可以通过实现这个接口方法 :
1 | public interface RejectedExecutionHandler { |
一、AbortPolicy
直接丢弃任务,抛出 RejectedExecutionExecption
异常,是默认策略。
RejectiedExectionExecption 属于 RuntimeException,会让你知道任务被拒绝了,我们便可以根据业务逻辑来决定是否重试或放弃任务。
二、CallerRunsPolicy
只用调用者所在的线程处理任务
当有新的任务提交后,线程池没有被关闭且没有执行能力,则把这个任务提交给提交任务的线程执行,也即谁提交的任务,谁负责执行。
好处是:
- 新提交的任务不会被丢弃,不会造成业务损失。
- 谁提交任务谁执行,相当于提交任务的线程负责任务的执行,如果执行任务是比较耗时的,那在这段时间内,提交任务的线程将会被占用,也就不会再提交新的任务,减缓了任务提交的速度,相当于一个负反馈。与此同时,线程池中的线程也可以充分利用这段时间来执行其他任务,腾出一定的空间,相当于给线程池一定的缓冲期。
缺点是:
当流量过大,线程池处理的任务一直在占用执行,没有设置超时,导致线程池被打满触发饱和策略 CallerRunsPolicy ,而此时主线程去处理提交任务,而任务又是阻塞的,主线程也会被 hang 住,导致主线程超时,引发故障。
三、DiscardOldestPolicy
丢弃等待队列中等待时间最长的任务,执行当前任务
线程池没有关闭但是没有执行能力,使用该策略则会丢弃任务队列中的头节点,通常是存活时间最长的任务,这种策略 与 AbortPolicy
策略不同之处在于它丢弃的不是最新提交的,而是队列中存活时间最长的。这样就可以腾出空间给新提交的任务线程,这样的风险就是会丢失数据。
四、DiscardPolicy
直接丢弃任务,不抛异常。
当线程池满了或者队列满了,新提交的线程任务会被直接丢弃,不会有任何的异常通知,同样的风险也是会丢失数据。