180  
查询码:00000263
Java线程池踩坑记
作者: 倪嗣成 于 2020年11月27日 发布在分类 / 物联网组 / 边缘接入网关 下,并于 2020年11月27日 编辑

先说下我们项目组在使用线程池时踩到的坑:

  1. 线程池的参数设置一定要结合具体的业务场景,区分I/O密集和CPU密集,如果是I/O密集型业务,核心线程数,workQueue等待队列,最大线程数等参数设置不合理不仅不能发挥线程池的作用,反而会影响现有业务
  2. 等待队列workQueue填满后,新创建的线程会优先处理新请求进来的任务,而不是去处理队列里的任务,队列里的任务只能等核心线程数忙完了才能被执行。有可能造成队列里的任务长时间等待,导致队列积压,尤其是I/O密集场景

  1. 如果需要得到线程池里的线程执行结果,使用future的方式,拒绝策略不能使用DiscardPolicy,这种丢弃策略虽然不执行子线程的任务,但是还是会返回future对象(其实在这种情况下我们已经不需要线程池返回的结果了),然后后续代码即使判断了future!=null也没用,这样的话还是会走到future.get()方法,如果get方法没有设置超时时间会导致一直阻塞下去! 

伪代码如下:

// 如果线程池已满,新的请求会直接执行拒绝策略
Future<String> future = executor.submit(() -> {
    // 业务逻辑,比如调用第三方接口等耗时操作放在线程池里执行
    return result;
});

// 主流程调用逻辑
if(future != null) // 如果拒绝策略设置不合理还是会走到下面代码
  future.get(超时时间); // 调用方阻塞等待结果返回,直到超时
但这不是根本原因,future是线程池返回的,伪代码如下:

Future<String> future = executor.submit(() -> {
    // 业务逻辑,比如调用第三方接口等耗时操作放在线程池里执行
    return result;
});

三. 改进

  1. 调整线程池参数,核心线程数基于线上接口的QPS计算,最大线程数参考线上tomcat的最大线程数配置,能够cover住高峰流量,队列设置的尽量小,避免造成任务挤压。关于线程数如何设置会在后续文章中单独讲解。
  2. 扩展线程池,封装原生JDK线程池ThreadPoolExecutor,增加对线程池各项指标的监控,包括线程池运行状态、核心线程数、最大线程数、任务等待数、已完成任务数、线程池异常关闭等信息,便于实时监控和定位问题。
  3. 重写线程池拒绝策略,主要也是记录超出线程池负载情况下的各项指标情况,以及调用线程的堆栈信息,便于排查分析,通过抛出异常方式中断执行,避免引用的future不为null的问题。
  4. 合理调整future.get超时时间,防止阻塞主线程时间过长。


 推荐知识

 历史版本

修改日期 修改人 备注
2020-11-27 11:49:34[当前版本] 倪嗣成 创建版本

  目录
    知识分享平台 -V 4.8.7 -wcp