如何在Java中使用ThreadPoolExecutor

答案:Java中通过ThreadPoolExecutor可自定义核心线程数、最大线程数、任务队列等参数来优化线程管理,使用execute或submit提交任务,需调用shutdown关闭线程池以释放资源。

在Java中使用ThreadPoolExecutor可以有效管理线程资源,提升程序性能。它允许你自定义线程池的行为,比如核心线程数、最大线程数、空闲线程存活时间等。下面介绍如何正确创建和使用ThreadPoolExecutor

创建ThreadPoolExecutor实例

ThreadPoolExecutor提供了多个构造函数,最完整的一个如下:

public ThreadPoolExecutor(
    int corePoolSize,
    int maximumPoolSize,
    long keepAliveTime,
    TimeUnit unit,
    BlockingQueue workQueue,
    ThreadFactory threadFactory,
    RejectedExecutionHandler handler
)

各参数含义如下:

  • corePoolSize:核心线程数,即使空闲也不会被回收(除非设置了allowCoreThreadTimeOut)
  • maximumPoolSize:线程池最大线程数
  • keepAliveTime:非核心线程的空闲存活时间
  • unit:存活时间的单位,如TimeUnit.SECONDS
  • workQueue:任务队列,用于存放待执行的任务
  • threadFactory:创建新线程的工厂,可自定义线程名称等属性
  • handler:拒绝策略,当任务无法提交时的处理方式

示例代码:

ThreadPoolExecutor executor = new ThreadPoolExecutor(
    2,           // 核心线程数
    4,           // 最大线程数
    60L,         // 空闲线程存活时间
    TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(10), // 队列大小
    Executors.defaultThreadFactory(),
    new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);

提交任务到线程池

你可以通过execute()submit()方法提交任务。

  • execute(Runnable):适用于

    不需要返回值的任务
  • submit(Runnable):返回一个Future对象,可用于跟踪任务状态
  • submit(Callable):提交有返回值的任务

示例:

// 提交无返回值任务
executor.execute(() -> {
    System.out.println("Task running in " + Thread.currentThread().getName());
});

// 提交有返回值任务 Future future = executor.submit(() -> { return "Hello from " + Thread.currentThread().getName(); });

try { String result = future.get(); // 获取结果 System.out.println(result); } catch (Exception e) { e.printStackTrace(); }

关闭线程池

使用完线程池后必须显式关闭,避免资源泄漏。

  • shutdown():不再接受新任务,等待已提交任务完成
  • shutdownNow():尝试中断正在执行的任务,并返回未执行的任务列表

推荐做法:

executor.shutdown();
try {
    if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
        executor.shutdownNow();
    }
} catch (InterruptedException e) {
    executor.shutdownNow();
    Thread.currentThread().interrupt();
}

常见配置建议

根据任务类型选择合适的线程池参数:

  • CPU密集型任务:线程数建议设置为CPU核心数 + 1
  • I/O密集型任务:可设置更多线程,例如2 * CPU核心数
  • 使用LinkedBlockingQueue时注意队列可能无限增长,建议指定容量
  • 生产环境建议自定义ThreadFactory以便排查问题

基本上就这些。合理使用ThreadPoolExecutor能显著提升应用并发能力,同时避免频繁创建线程带来的开销。关键是根据实际场景调整参数,并记得及时关闭资源。不复杂但容易忽略细节。