线程池大家都## 标题很熟悉,无论是平时的业务开发还是框架中间件都会用到,大部分都是基于JDK线程池ThreadPoolExecutor做的封装,
都会牵涉到这几个核心参数的设置:核心线程数,等待(任务)队列,最大线程数,拒绝策略等。
但如果线程池设置不当就会引起一系列问题, 下面就说下我最近碰到的问题。
案件还原
比如你有一个项目中有个接口部分功能使用了线程池,这个功能会去调用多个第三方接口,都有一定的耗时,为了不影响主流程的性能,不增加整体响应时间,所以放在线程池里和主线程并行执行,等线程池里的任务执行完通过future.get的方式获取线程池里的线程执行结果,然后合并到主流程的结果里返回,大致流程如下:
线程池参数为:
- coresize:50
- max:200
- queuesize:1
- keepalivetime:60s
- 拒绝策略为reject
假设每次请求提交5个task到线程池,平均每个task是耗时50ms
没过一会就收到了线程池满了走了拒绝策略的报错
结合你对线程池的了解,先思考下为什么
线程池的工作流程如下:
根据这个我们来列一个时间线
- 项目刚启动 第1次请求(每次5个task提交到线程池),创建5个核心线程
- 第2次请求 继续创建5个(共10个核心线程了)
- 直到第10次 核心线程数会达满50个
- 核心线程处理完之后核心线程会干嘛呢
根据 jdk1.8的线程池的源码:
线程池的线程处理处理了交给它的task之后,它会去getTask()
源码如下:
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
//加入Java开发交流君样:756584822一起吹水聊天
int wc = workerCountOf(c);
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
//注意这段
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
请注意上面代码中的bool类型的timed的赋值逻辑,
由于allowCoreThreadTimeOut默认为false,也就是说:
只要创建的线程数量超过了核心线程数,那么干完手上活后的线程(不管是核心线程,还是超过队列后新开的线程)就会走进
//线程状态为 timedwaiting
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)
由于我们上面步骤里面还没有超过coresize所以会走进
//线程状态为 waiting
workQueue.take(