记录一次由于线程池引发的生产事故

问题描述:

        服务器在线上环境正常运行了半个多月,每天的跑批都会发送一条开始跑批的短信记录,突然某一天没有发送短信。排查该问题,发现发送短信是通过线程池异步发送的,查找短信的发送日志记录,发现没有任何的日志,看日志也没有任何的报错信息。按道理来说如果说线程池的任务满了应该丢弃任务跑出异常的,因为线程池采取的策略就是AbortPolicy策略。


原因分析:

   现在系统还可以正常运行,不可能是因为内存溢出的问题,那么很可能就是线程池的问题,于是查看代码发现

	public static ExecutorService newFixedThreadPool(int nThreads) {
		String poolName = "default";
		//队列是无限队列
		//JDK的newFixedThreadPool 默认的拒绝策略为:AbortPolicy
        return new ThreadPoolMonitor(nThreads, nThreads,
        		0L,TimeUnit.MILLISECONDS, 
        		new LinkedBlockingQueue<Runnable>(),
        		new GcollThreadFactory(poolName),
        		new ThreadPoolExecutor.AbortPolicy(),poolName);
    }

这里是通过工具类生成的一个线程池,核心线程数与最大线程数设置的都是32,线程存活的时间是0秒,采取的是Linked阻塞队列,封装了一个线程池工厂用来给生成的线程起别名,采取的拒绝策略是AbortPolicy策略,丢弃任务并抛出异常。

我们看一下ThreaPoolExecutor的源码解释:

* <li> If corePoolSize or more threads are running, the Executor
 * always prefers queuing a request rather than adding a new
 * thread.</li>

翻译如下:如果超过线程池的核心线程池的数量在运行,再来的请求会进入到队列而不是添加一个新的请求。

new LinkedBlockingQueue<Runnable>(),这个方法的默认容量是Integer的最大值。

   public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }

    /**
     * Creates a {@code LinkedBlockingQueue} with the given (fixed) capacity.
     *
     * @param capacity the capacity of this queue
     * @throws IllegalArgumentException if {@code capacity} is not greater
     *         than zero
     */
    public LinkedBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        last = head = new Node<E>(null);
    }

所以只要前面32个核心线程都被一直占用住了,那么再来的请求会一直加入到阻塞队列中,所以没有报错,也没有采取拒绝策略。

通过jstack PID命令查看运行中的线程:

 发现这些线程都在同一处进行睡眠,我们排查代码发现:

发现是一个同事写的循环睡眠,这是一个定时任务,每次的定时任务执行到这里因为条件不满足都会卡在这里,一直睡眠,导致线程池的线程耗尽,最终来的新请求都处理不了也不报错。 


解决方案:

这里在循环等待某个条件的时候加一个超时时间就可以了,切记条件不满足的死循环。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值