无界线程池 好不好? 不好

地址
背景
在对线上查询接口进行压测时,当并发量上去后报出无法创建线程池的异常。
分析
之前的博客《 问题分析:java.lang.OutOfMemoryError:unable to create new native thread》里提到,线程创建使用的不是堆内存,而是实际物理内存。由于没有对应用的线程数资源进行监控,同时也没有对当时的内存资源进行监控,所以只能根据应用的日志进行分析。通过查看异常栈,发现是某个线程池在开启一个新的线程时会报出该异常,并且该线程池使用的是无界线程池,也就是说,当执行的任务一直没有完成时,线程池会持续不断的开启新的线程。
先来看看通过Executors创建无界线程池:

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

ThreadPoolExecutor构造方法的各个参数大家可以查阅下相关资料,这里简单描述下该线程池功能:
因为使用的是同步队列,并且核心线程数为0,所以当第一个任务到来时,不会放入同步队列,只要当前的线程数没有达到最大线程数,线程池就会创建一个新的线程来执行该任务。从参数里可以看到,最大线程数为int的最大值,所以一般情况下是不会达到最大线程数的。

在我们的应用中,使用该无界线程池的目的是为了从数据库中加载数据,并将数据放入到缓存中。其中,查询数据库涉及接近十张表,查询一张表就申请使用一个数据库连接,因为设置的数据库连接池最大为70,所以当并发量上来后,很容易消耗完数据库连接。因为每个任务都操作十张表,所以高并发下数据库连接资源就会形成瓶颈,导致各个任务执行非常缓慢。但需要注意的是,此处只是对数据库连接资源进行了争夺,但每一张表的数据查询是非常快的,这也是为什么通过DBA查询慢SQL时,没有发现特别的SQL原因。

一般来说,固定线程池适用于单个任务很小,并且很快能执行完得场景。像这种并发量上来后,对资源竞争严重的场景实际上并不适合。它会导致单个线程一直执行,当新的任务到达时,只会持续的开启新的线程,从而导致OOM。
结论
1、增加对应用线程数进行实时监控,一般来讲,应用线程数应该有一个峰值,而不是持续不断的增长。
2、线程池里的任务应该越快越好,否则应该考虑用消息队列的方式替代线程池的方案。
3、除非实际场景需要,尽量不要使用无界队列,而应该将任务缓存到队列中。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值