• 那些年我们踩到过的坑(一):为ThreadPoolExecutor 指定RejectedExecutionHandler需要注意的坑


    昨天下午公司的短信发送服务挂掉,查日志发现有些短信服务提供商的服务器time out。马上联系对方,确认服务已经恢复正常,我们立马重启服务,恢复正常。

    我们的短信服务是起一个线程T1从redis list去拿消息,然后创建一个发送短信的任务线程扔到线程池里执行,每一个发送短信的任务都会连接服务商的服务,time out就是从这里抛出来的,可是连接time out怎么能导致我们的服务挂掉呢? 还好当时做了thread dump,分析了下dump 文件,发现线程T1挂掉了,看代码发现该线程的run方法块里面只catch住了Exception,极有可能是有Throwable的错误抛了出来,导致该线程意外终止。我当时能够想到的有OutOfMemmory 跟 StackOverflow,经过分析定位到以下我们封装的线程池。

    private ThreadPoolExecutor threadPoolTaskExecutor = new ThreadPoolExecutor(30, 100, 30, 
                TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(4), new ThreadFactoryBuilder("SmsServiceManager-threadPoolTaskExecutor"), 
                new RejectedExecutionHandler() {
                    @Override
                    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                        if (!executor.isShutdown()) {
                            executor.execute(r);
                        }
                    }
                });

     注意该类指定了RejectedExecutionHandler,如果任务被reject那么将任务继续添加到线程池。WTF!!!!真暴力啊,线程池拒绝了你的任务,你不做任何处理又把该任务扔进线程池,这就是stack over flow的原因!!!线程池一共有30个worker线程,当时正好某些服务商的短信服务出问题,所以这30个worker线程一直卡在连接对方服务上,当线程池中任务的数量超过100个,那么后来的任务将会被线程池reject,而你被reject后将该任务再一次扔进线程池,产生死循环直到线程T1栈溢出。

    最后我们的解决方案是:

    1. 在线程T1的run方法块里面catch住Throwable。

    2. 任务被线程池reject后将消息重新放到redis队列里。

    这两天刚刚接手短信这块,坑很多,且行且小心。

  • 相关阅读:
    【重构学习】12 重构学习感想
    【重构学习】11 大型重构
    【重构学习】10 继承关系的重构
    【重构学习】09 函数调用的重构
    【重构学习】08 条件表达式的重构
    嵊州D4T1 翻车 rollover 真的翻车了
    计算圆内格点数
    嵊州D3T2 福尔贝斯太太的快乐夏日 summer
    嵊州D3T3 light
    嵊州D3T1 山魔 烙饼问题
  • 原文地址:https://www.cnblogs.com/longzhaoyu/p/4539341.html
Copyright © 2020-2023  润新知