java教程笔记(十四)-线程池

Java 的线程池(ThreadPool)是并发编程中用于管理和复用线程的重要机制。它通过预先创建一组线程并重复使用它们来执行多个任务,从而减少频繁创建和销毁线程的开销,提高程序性能。

1.线程池的核心概念

1. 线程池的作用

  • 降低资源消耗:避免频繁创建/销毁线程。
  • 提高响应速度:任务到达后可以直接使用已有线程执行。
  • 提高线程可管理性:统一管理线程生命周期、限制最大并发数、防止资源耗尽。

2.Java 中线程池的实现类:ThreadPoolExecutor

ThreadPoolExecutor 是 Java 标准库中最核心的线程池实现类,构造函数如下:

推荐通过构造函数配置参数自定义创建线程池

复制代码
ThreadPoolExecutor(
int corePoolSize, 
int maximumPoolSize,
 long keepAliveTime, 
TimeUnit unit, 
BlockingQueue<Runnable> workQueue, 
ThreadFactory threadFactory, 
RejectedExecutionHandler handler)

参数说明:

参数名 描述
corePoolSize 核心线程数(常驻线程数)
maximumPoolSize 最大线程数
keepAliveTime 非核心线程空闲超时时间
unit 超时单位
workQueue 任务队列
threadFactory 线程工厂,用于创建新线程
handler 拒绝策略

3.线程池的工作流程

  1. 提交任务时:

    • 如果当前线程数 < corePoolSize,创建新线程处理任务。
    • 如果当前线程数 >= corePoolSize,将任务放入队列。
    • 如果队列已满且当前线程数 < maximumPoolSize,创建新线程处理任务。
    • 如果队列已满且线程数 >= maximumPoolSize,执行拒绝策略。
  2. 空闲线程会根据 keepAliveTime 判断是否回收(非核心线程优先回收)。

4.常见的线程池类型(来自 Executors 工具类)

Java 提供了几个常用使用 Executors 工具类创建线程池的方法

线程池类型 特点 使用场景
newFixedThreadPool 核心线程数 = 最大线程数 ,所有线程都是核心线程 线程不会超时回收,会一直存活 使用无界队列 LinkedBlockingQueue 适用于负载较重、任务量稳定的系统
newCachedThreadPool 核心线程数为 0,最大线程数为 Integer.MAX_VALUE 空闲线程超时时间为 60 秒,超过时间会被回收 使用同步队列 SynchronousQueue 适用于执行大量短期异步任务
newSingleThreadExecutor 只有一个线程工作的线程池 所有任务按顺序执行(保证串行) 使用无界队列 LinkedBlockingQueue 保证任务串行执行,避免并发问题
newScheduledThreadPool 支持定时和周期性任务调度 使用延迟队列 DelayedWorkQueue 可以提交 RunnableCallable 任务,并指定延迟或周期执行 定时调度、延迟执行等场景
newWorkStealingPool (JDK8+) 使用 ForkJoinPool 实现,支持工作窃取算法 大规模并行任务调度

示例代码:

复制代码
ExecutorService executor = Executors.newFixedThreadPool(5); 
executor.submit(() -> System.out.println("Task executed"));

5.拒绝策略(RejectedExecutionHandler)

当线程池无法处理新任务时,会调用拒绝策略。常见的内置策略有:

策略 行为
AbortPolicy(默认) 抛出 RejectedExecutionException 异常
CallerRunsPolicy 由提交任务的线程自己执行任务
DiscardPolicy 直接丢弃任务,不抛异常
DiscardOldestPolicy 丢弃队列中最老的任务,尝试重新提交当前任务

自定义拒绝策略:

复制代码
RejectedExecutionHandler handler = (r, executor) -> { System.out.println("Task rejected: " + r.toString()); };

6.创建线程池之后,如何使用

线程池创建之后,主要通过提交任务来使用它。Java 中的线程池(ExecutorService)提供了多种方式来提交任务,并支持同步、异步、定时等执行模式。

1.提交任务的方式

1. execute(Runnable command)

  • 用途:提交一个不需要返回结果的任务。
  • 特点
    • 异步执行

    • 不返回结果

    • 不抛出异常(需手动捕获)

      ExecutorService executor = Executors.newFixedThreadPool(4);
      executor.execute(() -> { System.out.println("Task executed in thread: " + Thread.currentThread().getName()); });

2. submit(Runnable task)

  • 用途 :提交一个没有返回值的任务,返回一个 Future 对象。
  • 特点
    • 可用于跟踪任务是否完成

    • 调用 future.get() 可以阻塞等待任务结束

      Future<?> future = executor.submit(() -> { System.out.println("Running task..."); }); future.get(); // 阻塞直到任务完成

3. submit(Callable<T> task)

  • 用途:提交一个有返回值的任务。
  • 特点
    • 返回泛型结果

    • 支持异常抛出

    • 使用 Future.get() 获取结果或捕获异常

      Future future = executor.submit(() -> { return 42; });
      System.out.println("Result: " + future.get()); // 输出 42

4. invokeAll(Collection<? extends Callable<T>> tasks)

  • 用途 :批量提交多个 Callable 任务,等待所有任务完成。
  • 特点
    • 返回 List<Future<T>>

    • 可设置超时时间

      List<Callable> tasks = Arrays.asList( () -> "Task 1", () -> "Task 2", () -> "Task 3" );
      List<Future> results = executor.invokeAll(tasks);
      for (Future result : results) { System.out.println(result.get()); }

5. invokeAny(Collection<? extends Callable<T>> tasks)

  • 用途:提交多个任务,只要有一个任务完成就返回其结果,其他任务取消。
  • 特点
    • 提升响应速度

    • 常用于"最先完成者胜出"的场景

      String result = executor.invokeAny(tasks); // 返回第一个完成的结果 System.out.println("First completed: " + result)

2.定时任务调度(适用于 ScheduledExecutorService

如果使用的是 newScheduledThreadPool,可以使用以下方法:

1. schedule(Runnable command, long delay, TimeUnit unit)

  • 用途:延迟执行一次任务

    ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
    scheduler.schedule(() -> System.out.println("Delayed task"), 3, TimeUnit.SECONDS);

2. scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)

  • 用途:固定频率执行任务(即使上一次任务未完成,也会在周期到达时启动下一次)

    scheduler.scheduleAtFixedRate(() -> { System.out.println("Fixed rate task"); }, 0, 1, TimeUnit.SECONDS);

3. scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)

  • 用途:固定延迟执行任务(每次任务完成后,再等待指定时间)

    scheduler.scheduleWithFixedDelay(() -> { System.out.println("Fixed delay task"); }, 0, 1, TimeUnit.SECONDS);

3.关闭线程池

线程池使用完毕后必须关闭,否则可能导致程序无法正常退出。

1. shutdown()

  • 作用:不再接受新任务,但会等待已提交任务执行完毕。

executor.shutdown();

2. shutdownNow()

  • 作用:尝试立即停止所有正在执行的任务,并返回尚未执行的任务列表。

List<Runnable> pendingTasks = executor.shutdownNow();

3. 等待关闭完成(可选)

复制代码
if (!executor.isShutdown()) { 
executor.shutdown(); 
} 
try { if (!executor.awaitTermination(60, TimeUnit.SECONDS)) { 
executor.shutdownNow();
 } 
} 
catch (InterruptedException e) { executor.shutdownNow(); }

4.完整示例

复制代码
import java.util.concurrent.*;
 public class ThreadPoolUsage {
 public static void main(String[] args) throws Exception { 
ExecutorService executor = Executors.newFixedThreadPool(4); 
// 提交 Runnable 任务
 executor.execute(() -> System.out.println("Hello from execute")); 
// 提交 Callable 任务并获取结果 
Future<Integer> future = executor.submit(() -> { return 100; }); System.out.println("Callable result: " + future.get()); // 定时任务 ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2); scheduler.schedule(() -> System.out.println("Delayed task"), 2, TimeUnit.SECONDS); 
// 关闭线程池 
executor.shutdown(); 
scheduler.shutdown();
 } 
}

7.总结

操作 方法 说明
提交无返回任务 execute(Runnable) 最简单的异步执行方式
提交无返回任务 submit(Runnable) 返回 Future 可判断是否完成
提交有返回任务 submit(Callable) 支持返回值和异常处理
批量提交任务 invokeAll() 返回所有任务的 Future 列表
任一任务完成即返回 invokeAny() 适用于多任务中取最快结果
定时/周期任务 schedule*() 适用于 ScheduledExecutorService
关闭线程池 shutdown() / shutdownNow() 必须显式调用

合理使用这些方法,可以充分发挥线程池的优势,提高并发性能与资源利用率。

8.线程池的监控与关闭

1. 常用监控方法:

方法 说明
getPoolSize() 获取当前线程池中的线程数量
getActiveCount() 获取正在执行任务的线程数
getCompletedTaskCount() 获取已完成的任务数
getQueue() 获取等待执行的任务队列
boolean isShutdown() 判断线程池是否已关闭
boolean isTerminated() 判断线程池是否完全终止(所有任务执行完并关闭)
boolean awaitTermination(long timeout, TimeUnit unit) 等待线程池终止

9.最佳实践

  • 合理设置核心线程数和最大线程数,避免资源浪费或线程爆炸。
  • 选择合适的任务队列 (如 LinkedBlockingQueue, ArrayBlockingQueue)。
  • 避免任务堆积,设置合理的拒绝策略。
  • 及时关闭线程池,防止内存泄漏。
  • 使用 FutureCompletableFuture 获取任务结果或异常信息
相关推荐
浦信仿真大讲堂4 分钟前
从重复操作到自动化闭环:如何让 CST 与 Python 真正协同起来
python·自动化·cst·仿真软件·达索软件
萧瑟余晖9 分钟前
Java深入解析篇九之NIO详解
java·网络·nio
龙仔72524 分钟前
人大金仓 KingbaseES V8 只读账号创建完整运维笔记
运维·笔记·sql·人大金仓
韭菜炒鸡肝天29 分钟前
VTK开发笔记(一):VTK介绍,Qt..+VSx+VTK.编译
开发语言·笔记·qt
The Chosen One98529 分钟前
高进度算法模板速记(待完善)
java·前端·算法
Gu Gu Study32 分钟前
ScoutLoop开放域深度研究引擎(agent的初步设计想法)
人工智能·python
卷无止境1 小时前
写代码这件事,到底该讲究点什么?
后端·python
卷无止境1 小时前
循环复杂度到底在算什么,Python 代码怎么才能写得让人一看就懂
后端·python
lpfasd1231 小时前
MediaCrawler 项目深度分析
chrome·python·chrome devtools
Dxy12393102162 小时前
Python项目打包成EXE完整教程(PyInstaller实战避坑)
开发语言·python