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

发布于:2025-06-10 ⋅ 阅读:(15) ⋅ 点赞:(0)

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

可以提交 Runnable 或 Callable 任务,并指定延迟或周期执行

定时调度、延迟执行等场景
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<Integer> 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<String>> tasks = Arrays.asList( () -> "Task 1", () -> "Task 2", () -> "Task 3" ); 
List<Future<String>> results = executor.invokeAll(tasks); 
for (Future<String> 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.最佳实践

  • 合理设置核心线程数和最大线程数,避免资源浪费或线程爆炸。
  • 选择合适的任务队列(如 LinkedBlockingQueueArrayBlockingQueue)。
  • 避免任务堆积,设置合理的拒绝策略。
  • 及时关闭线程池,防止内存泄漏。
  • 使用 Future 或 CompletableFuture 获取任务结果或异常信息