当前位置: 首页 > 科技观察

Java线程池拒绝策略解析

时间:2023-03-12 12:49:55 科技观察

为什么会有拒绝策略?在线程池工作中,当任务负载过大,超过了系统的实际承载能力时,如果稍不注意,系统很可能会崩溃,所以jdk提供了四种内置的拒绝策略线程池的出现可以合理的解决这个问题。当线程池中的线程用完不能再创建,等待队列也满了,此时如果有新的任务,就会触发其中一种拒绝策略。1、CallerRunsPolicy(调用者运行策略)一般用在不允许失败,性能要求不高,并发量小的场景,因为一般情况下不会关闭线程池,也就是提交的任务肯定会可以运行,但是因为是调用者线程自己执行的,当多次提交任务时,会阻塞后续任务的执行,性能和效率自然会变慢。当触发拒绝策略时,只要线程池没有关闭,就会由当前提交任务的线程处理。2.AbortPolicy(中止策略)当触发拒绝策略时,会直接抛出拒绝执行的异常。abort策略是直接中断当前的执行过程。它没有特殊的使用场景,但有一点是正确处理抛出的异常。ThreadPoolExecutor中的默认策略是AbortPolicy。ThreadPoolExecutor系列的ExecutorService接口没有显示拒绝策略,所以默认就是这个。但是需要注意的是,ExecutorService中的线程池实例队列是无界的,也就是说内存爆了不会触发拒绝策略。当你自己自定义线程池实例时,在使用该策略时必须处理策略触发时抛出的异常,因为它会打断当前的执行过程。3.DiscardPolicy(丢弃策略)如果你提交的任务无关紧要,你可以使用它。因为它只是一个空的实现,它会默默地吞噬你的任务。它只是安静地丢弃任务而不触发任何操作。所以这个策略基本没用。4.DiscardOldestPolicy(丢弃旧的策略)这个策略还是会丢弃任务,丢弃的时候会静默,但是丢弃的是旧的未执行的任务,是优先级更高的待执行任务。基于这个特性,我能想到的场景就是发布和修改消息。消息发布后,还没有执行。这时,更新的消息又来了。此时未执行消息的版本低于当前版本。被丢弃了。因为队列中可能会有消息版本较低的消息会被排队执行,所以在实际处理消息的时候需要做好消息版本的比较。这种拒绝策略是喜新厌旧的拒绝策略。是否采用这种拒绝策略,必须根据实际业务是否允许丢弃旧任务来慎重权衡。第三方实现的拒绝策略可以看dubbo中的线程拒绝策略。dubbo的工作线程在触发线程拒绝时,主要做了三件事。原理是尽量让用户知道触发线程拒绝策略的真正原因。输出一个警告级别的日志,日志内容是线程池的详细设置参数,线程池的当前状态,以及当前被拒绝的任务的一些详细信息。可以说,这个日志,用过dubbo,有过生产运维经验的人,或多或少都看过。这个日志简单来说就是一个日志打印的模型,其他的日志打印模型还有spring。得益于如此详细的日志,很容易定位问题。Netty中的线程池拒绝策略Netty中的实现与JDK中的CallerRunsPolicy非常相似。不同之处在于调用者运行策略是直接在调用者线程上执行的任务。而Netty会创建一个新的线程来处理它。因此,可以扩展Netty的使用范围,支持高效、高性能的场景。但是也要注意,在Netty的实现中,创建线程的时候是没有判断约束的。pinpoint中的线程池拒绝策略pinpoint的拒绝策略实现非常有特色和独到之处。他定义了一个拒绝策略链,它包装了一个拒绝策略列表。当拒绝策略被触发时,策略链中的rejectedExecution会被一一执行。最后希望大家在阅读本文后,对java线程池拒绝策略有更详细的了解,可以根据不同的使用场景灵活使用。