线程池(Thread Pool)是一种非常重要的并发编程工具,它通过预先创建一定数量的线程并重复使用这些线程来执行任务,从而减少线程的频繁创建和销毁带来的性能开销。Java 提供了强大的线程池支持,主要通过 java.util.concurrent 包中的 Executor 框架来实现。

一、Executor 框架

        Java 的 Executor 框架是 Java 并发包(java.util.concurrent)中用于简化多线程编程的一个重要组件。它提供了一种将​​任务提交​​与​​任务执行​​分离的机制,使开发者可以更专注于业务逻辑,而不必过多关注线程的创建、管理和调度。  

Executor 框架主要由以下几个核心接口和类组成:

1. Executor 接口

这是 Executor 框架的最顶层接口,定义了一个非常简单的方法:

void execute(Runnable command);
  • 用于提交一个 Runnable 类型的任务给线程池或线程执行。
  • 它不提供任务的返回值,也不提供对任务执行状态的控制。 

2. ExecutorService 接口

  ExecutorService 是 Executor 的子接口,扩展了更多功能,提供了对线程池的更全面管理能力。它的主要方法包括:

2.1 提交任务:
  • void execute(Runnable command); (继承自 Executor
  • <T> Future<T> submit(Callable<T> task);:提交一个有返回值的任务(Callable)。
  • Future<?> submit(Runnable task);:提交一个没有返回值的任务(Runnable)。
  • <T> Future<T> submit(Runnable task, T result);:提交一个 Runnable 任务,并指定返回结果。
2.2 关闭线程池:
  • void shutdown();:平缓关闭线程池,不再接受新任务,但会继续执行队列中的任务。
  • List<Runnable> shutdownNow();:立即关闭线程池,尝试停止所有正在执行的任务,并返回等待执行的任务列表。
2.3 其他方法:
  • boolean isShutdown();:判断线程池是否已关闭。
  • boolean isTerminated();:判断线程池是否已完全终止(所有任务都已完成)。
  • boolean awaitTermination(long timeout, TimeUnit unit):阻塞等待线程池终止,直到超时或线程池完全终止。

ExecutorService 是实际开发中最常用的接口,通常通过线程池(如 ThreadPoolExecutor)来实现。

3. ThreadPoolExecutor(线程池核心实现)

        ThreadPoolExecutor是Executor框架最核心、最灵活的实现,允许高度定制线程池的行为。

核心参数一览表

参数

类型

必填

说明

推荐配置建议

相关影响

​corePoolSize​

int

线程池中保持的核心线程数量,即使它们处于空闲状态

CPU密集型:CPU核心数+1
IO密集型:CPU核心数×2或更高

决定线程池的基础并发能力,核心线程默认会一直存活(除非设置allowCoreThreadTimeOut)

​maximumPoolSize​

int

线程池允许存在的最大线程数量

通常为核心线程数的1-2倍(视任务类型而定)

当工作队列满且线程数小于此值时,会创建新线程执行任务

​keepAliveTime​

long

当线程数超过核心线程数时,多余空闲线程的存活时间

通常设置为30-60秒

控制非核心线程的空闲回收时间,合理设置可平衡资源利用和响应速度

​unit​

TimeUnit

keepAliveTime的时间单位

TimeUnit.SECONDS(常用)

配合keepAliveTime使用,定义时间单位

​workQueue​

BlockingQueue<Runnable>

用于保存等待执行的任务的阻塞队列

​CPU密集型​​:有界队列(如ArrayBlockingQueue)
​IO密集型​​:无界队列或大容量队列

决定任务排队策略,直接影响线程创建行为和拒绝策略触发时机

​threadFactory​

ThreadFactory

用于创建新线程的工厂

建议自定义,为线程设置有意义的名字

影响线程的创建方式、名称、优先级等,便于问题排查和监控

​handler​

RejectedExecutionHandler

当线程池和工作队列都满时的拒绝策略

根据业务需求选择:AbortPolicy(默认)、CallerRunsPolicy、DiscardPolicy、DiscardOldestPolicy

决定系统过载时的处理方式,影响系统的健壮性和可靠性

 构造函数源码:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }


public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }


public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

二、线程池 

1.线程池的创建方式

1.1. 直接使用ThreadPoolExecutor构造函数(推荐

int corePoolSize = 5;
int maximumPoolSize = 10;
long keepAliveTime = 60;
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(100);

ThreadPoolExecutor executor = new ThreadPoolExecutor(
    corePoolSize,
    maximumPoolSize,
    keepAliveTime,
    unit,
    workQueue,
    Executors.defaultThreadFactory(),
    new ThreadPoolExecutor.AbortPolicy()
);

1.2. 线程池工作流程

  • ​提交任务时​​:

    • 如果当前线程数 < corePoolSize,则创建新线程执行任务

    • 如果线程数 ≥ corePoolSize,将任务放入工作队列

    • 如果队列已满且线程数 < maximumPoolSize,则创建新线程执行任务

    • 如果队列已满且线程数 ≥ maximumPoolSize,则执行拒绝策略

  • ​线程执行任务​​:

    • 线程从队列中获取任务并执行

    • 执行完成后,线程会尝试从队列中获取下一个任务

  • ​线程空闲处理​​:

    • 核心线程默认会一直存活等待新任务

    • 非核心线程(线程数 > corePoolSize)在空闲超过keepAliveTime后会被终止

2. 使用Executors工厂方法(不推荐) 

Executors工厂类提供的几种常用线程池创建方法:

    线程池类型

    创建方法

    特点

    潜在问题

    ​固定大小线程池​

    newFixedThreadPool(int nThreads)

    线程数量固定,线程空闲也不会被回收

    ​队列无界,可能导致OOM(内存溢出)​​,任务可能长时间等待

    ​单线程线程池​

    newSingleThreadExecutor()

    只有一个工作线程,保证任务按顺序执行(FIFO)

    ​队列无界,可能导致OOM​​,单线程故障会导致任务阻塞

    ​可缓存线程池​

    newCachedThreadPool()

    线程数量根据需求自动调整,空闲线程60秒后回收

    ​线程数可能无限增长(达到Integer.MAX_VALUE)​​,可能导致资源耗尽

    ​定时任务线程池​

    newScheduledThreadPool(int corePoolSize)

    支持定时及周期性任务执行

    最大线程数理论上无限制,长期运行可能积累大量任务

    ​单线程定时任务池​

    newSingleThreadScheduledExecutor()

    单线程执行定时任务,保证任务顺序执行

    单线程故障会影响后续任务,无界队列可能积累大量任务

    ​3.注意

    《阿里巴巴开发手册》强制要求线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样 的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

    Logo

    汇聚全球AI编程工具,助力开发者即刻编程。

    更多推荐