【java-基础】线程池ThreadPoolExecutor 与优雅退出

线程池创建

主要有两种方式一种是通过Executors工具类快速创建,某些代码规范不推荐这么做,是因为会过分的隐藏线程池参数细节,让小白使用的时候不清楚线程池在不同状态下的策略

另一种就是通过ThreadPoolExecutor的构造函数如下:

public ThreadPoolExecutor(int corePoolSize, // 核心线程数量
                              int maximumPoolSize,      // 最大线程池数量
                              long keepAliveTime,        // 非核心线程数存活时间
                              TimeUnit unit,                  // 非核心线程数存活时间单位
                              BlockingQueue<Runnable> workQueue, // 线程满负荷时任务队列
                              ThreadFactory threadFactory,                    // 线程创建工厂
                              RejectedExecutionHandler handler)        // 拒绝策略

需要注意的是默认workQueue 是无限大的,很容易出现内存溢出,RejectedExecutionHandler 默认策略是,如果线程和阻塞队列都满了,直接丢弃任务,应该换成ThreadPoolExecutor.CallerRunsPolicy 策略,该策略会让调用线程去执行该任务从而达到将阻塞蔓延到调用方的效果

优雅退出

当程序退出时,我们很容易想到使用executor.shutdown()的方式退出,但实际上这个方式并不能优雅退出,而官方也在javadoc上很明显的指出这一点

Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted. Invocation has no additional effect if already shut down.
This method does not wait for previously submitted tasks to complete execution. Use
awaitTermination to do that.

使用该方法仅仅可以保证不去接受新的任务,处理当前正在处理的任务和队列的任务,但是,但是该方法并不阻塞,因为默认的线程是非守护线程,如果主线程都退出了,那么程序会直接退出,未执行完的任务都不会再执行,还是会丢任务。

方案一: 是使用ThreadFactory创建守护线程,但是该任务可能依赖其他的springbean,如果下游的bean已经销毁,那么该线程还是无法正确执行任务

方案二: (推荐) 接受javadoc的建议使用executor.awaitTermination()进行阻塞,直到所有任务都进行完毕,主线程才会继续执行,该方法可以传递一个时间参数,决定最多阻塞时间,达到这个时间后无论队列中任务是否执行完都会继续。

更推荐的是不指定,如果任务处理不完就一直等待,把强行杀死进程丢失数据的决定交给用户,如果迟迟不能消费完任务,要不就是bug,要不就是下游出现问题,这两种情况都不应该让程序寿终正寝,应该把问题暴露出来,如果用户确实想要结束,可以随时使用kill -9 结束进程

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容