【线程池】线程池的4种拒绝策略

邓敏 2021年12月16日 183次浏览

线程被拒绝的场景

image.png

结合上面的图,我们思考一个问题,当我们创建一个线程池的时候,他会有最大的线程数,和线程任务缓存数。如果系统的线程任务突然暴增,这个时候,线程池的线程达到最大值,线程队列中的缓存数也达到了最大值,这个时候,没有挤进线程池的该怎么办。

在使用线程池的时候,可能会遇到两种情况,导致线程会需要被拒绝。

  • 调用 shutdown 等方法关闭线程池后,即便此时可能线程池内部依然有没执行完的任务正在执行,但是由于线程池已经关闭,此时如果再向线程池内提交任务,就会遭到拒绝。
  • 线程池没有能力继续处理新提交的任务,也就是工作已经非常饱和的时候。

线程池的拒绝Handler

其实在这里,我们其实可以想到线程池的初始配置中有一个配置handler,这个其实就是为了解决这种问题而出现的,在线程池中,除了我们自己可以定义一个拒绝Handler策略,线程池其实也内置了四种不同的拒绝策略供我们使用。

线程策略

AbortPolicy(异常策略)

在遇到拒绝任务时,会直接抛出一个类型为 RejectedExecutionException 的 RuntimeException,所以我会叫他为异常策略,因为他会抛出异常。这个异常你可以捕获起来,然后做你想做的事。

DiscardPolicy(丢弃策略)

这个策略就比较悲伤了,当线程满了之后,后面想进来的线程会被直接丢弃掉,丢弃掉的线程后面不会再执行,也不会有通知,更不会有记录,所以这个策略比较危险,他会让线程人间蒸发。这样大家都不知道他有没有发生过。如果发生在业务上,可能会造成业务数据的丢失。

DiscardOldestPolicy (牺牲策略)

这个就比较残忍,虽然不会丢弃掉想要挤进来的线程,但是他会把线程队列中存活时间久的线程给丢弃掉,让想挤进来的线程去替代他。有点像渣男。可以把它叫为渣男策略

CallerRunsPolicy(执行策略)

这个策略就比较负责了,当线程池满了之后,想要进来的线程不会被丢弃,他会直接让提交线程的线程去执行这个线程任务,也是不进入到线程池,你直接就地解决吧。更简单的理解为,当遇到了这个策略,相当于这个线程池新增了一个线程资源。
这样的方式有两点好处

  • 新提交的任务不会被丢弃,也就不会造成业务上的损失。
  • 新提交的任务会让提交者的线程去执行,这个时候提交者的线程就会被阻塞一段时间,这段时间其实会给线程池减少一定程度的压力,减缓了提交者的去提交线程的速度。