java封装好的线程池

Java 中封装好的线程池主要通过Executors工具类来创建,底层均是基于ThreadPoolExecutor类实现的。常见的有以下几种:

  1. 固定大小线程池 (newFixedThreadPool)
    创建一个固定长度的线程池,可以控制最大并发数。当线程都在忙时,新提交的任务会进入等待队列,直到有空闲线程。

  2. 可缓存线程池 (newCachedThreadPool)
    创建一个可根据需要创建新线程的线程池。如果线程池的当前规模超过了处理需求,它会灵活地回收空闲线程;如果没有可回收的线程,则会创建新线程。

  3. 单线程线程池 (newSingleThreadExecutor)
    创建一个单线程化的线程池,它只会使用唯一的工作线程来执行任务,保证所有任务按照指定顺序(如 FIFO)执行。

  4. 定时任务线程池 (newScheduledThreadPool)
    创建一个固定长度的线程池,支持定时及周期性任务的执行。

但是在生产环境中,强烈不推荐直接使用Executors工具类提供的这四种线程池newFixedThreadPoolnewCachedThreadPoolnewScheduledThreadPoolnewSingleThreadExecutor),原因:

为什么不推荐?各线程池的致命隐患如下:

  1. newFixedThreadPool 与 newSingleThreadExecutor
    • 隐患底层使用了无界的LinkedBlockingQueue(容量默认为Integer.MAX_VALUE)。当任务提交速度大于处理速度时,任务会无限堆积,最终耗尽 JVM 堆内存,导致OutOfMemoryError
  2. newCachedThreadPool
    • 隐患:最大线程数被设置为Integer.MAX_VALUE,且使用不缓冲的SynchronousQueue。在突发高并发流量下,它会无节制地创建海量线程,瞬间耗尽线程栈内存或导致 CPU 频繁上下文切换,引发系统假死或宕机。线程数失控导致 OOM。
  3. newScheduledThreadPool
    • 隐患:底层使用的是无界的延迟队列DelayedWorkQueue。如果调度任务执行缓慢或发生阻塞,后续所有的延时任务都会持续积压在队列中,同样存在内存泄漏和 OOM 的风险

生产环境的正确做法

在生产环境中,必须绕过Executors工厂类,手动构造ThreadPoolExecutor,显式配置所有核心参数,确保资源边界可控:

  • 核心与最大线程数:根据业务是 CPU 密集型还是 IO 密集型,合理设置corePoolSizemaximumPoolSize
  • 有界队列:必须使用有界阻塞队列(如ArrayBlockingQueue),并明确指定容量上限,防止任务无限堆积。
  • 拒绝策略:显式配置拒绝策略(如CallerRunsPolicy),在队列满时提供缓冲或快速失败机制,防止系统雪崩。
  • 自定义线程工厂:为线程设置有业务含义的名称(如order-async-%d),便于在排查日志和进行线程 Dump 时快速定位问题归属。
  • 专用定时线程池:对于定时/周期任务,应直接new ScheduledThreadPoolExecutor(...)并显式指定核心线程数,避免单点故障。