线程池学习
线程池是 Java 并发编程中非常重要的工具,它可以有效地管理多个线程,减少线程创建和销毁的开销,并提高系统的性能和稳定性。下面我们来详细介绍线程池的相关知识,包括如何判断线程池任务执行完成、线程池的状态及其转变。
1. 线程池简介
线程池(ThreadPool)是一种线程管理机制,它通过维护一组线程来执行任务,避免了频繁创建和销毁线程的开销。Java 提供了 java.util.concurrent
包来支持线程池的实现,其中最常用的是 ThreadPoolExecutor
。
线程池的核心参数
- 核心线程数(corePoolSize):线程池中保持的最小线程数。
- 最大线程数(maximumPoolSize):线程池中允许的最大线程数。
- 工作队列(workQueue):用于存放待执行任务的阻塞队列。
- 线程工厂(threadFactory):用于创建新线程的工厂。
- 拒绝策略(rejectedExecutionHandler):当任务无法被线程池接受时的处理策略。
2. 如何判断线程池任务执行完成?
判断线程池任务是否执行完成,可以通过以下几种方式:
2.1 使用 isTerminated()
方法
isTerminated()
方法用于判断线程池是否已经完全终止。- 调用
shutdown()
方法后,线程池会等待所有任务执行完成,然后进入终止状态。
ExecutorService executor = Executors.newFixedThreadPool(5);
// 提交任务
executor.submit(() -> System.out.println("Task 1"));
executor.submit(() -> System.out.println("Task 2"));
// 关闭线程池
executor.shutdown();
// 等待所有任务执行完成
while (!executor.isTerminated()) {
// 等待
}
System.out.println("All tasks are finished.");
2.2 使用 awaitTermination()
方法
awaitTermination()
方法会阻塞当前线程,直到线程池中的所有任务执行完成或超时。
ExecutorService executor = Executors.newFixedThreadPool(5);
// 提交任务
executor.submit(() -> System.out.println("Task 1"));
executor.submit(() -> System.out.println("Task 2"));
// 关闭线程池
executor.shutdown();
// 等待所有任务执行完成,最多等待 1 分钟
if (executor.awaitTermination(1, TimeUnit.MINUTES)) {
System.out.println("All tasks are finished.");
} else {
System.out.println("Timeout before all tasks are finished.");
}
2.3 使用 Future
对象
- 如果任务是通过
submit()
方法提交的,可以返回一个Future
对象,通过Future.isDone()
方法判断任务是否完成。
ExecutorService executor = Executors.newFixedThreadPool(5);
Future<?> future = executor.submit(() -> {
System.out.println("Task running");
Thread.sleep(1000);
return null;
});
// 判断任务是否完成
while (!future.isDone()) {
System.out.println("Task is still running...");
Thread.sleep(200);
}
System.out.println("Task is finished.");
executor.shutdown();
3. 线程池的状态
线程池的状态由 ThreadPoolExecutor
的内部类 AtomicInteger
变量 ctl
表示,它同时记录了线程池的状态和线程数量。线程池的状态有以下几种:
3.1 RUNNING
- 状态值:
RUNNING = 111
- 描述:线程池正常运行,可以接受新任务并处理队列中的任务。
- 状态转换:
- 线程池刚创建时处于 RUNNING 状态。
- 调用
shutdown()
方法后,线程池会从 RUNNING 状态转换为 SHUTDOWN 状态。
3.2 SHUTDOWN
- 状态值:
SHUTDOWN = 000
- 描述:线程池不再接受新任务,但会继续处理队列中的任务。
- 状态转换:
- 调用
shutdown()
方法后,线程池从 RUNNING 状态转换为 SHUTDOWN 状态。 - 当队列中的任务处理完成后,线程池会转换为 TIDYING 状态。
- 调用
3.3 STOP
- 状态值:
STOP = 001
- 描述:线程池不再接受新任务,也不会处理队列中的任务,并会中断正在执行的任务。
- 状态转换:
- 调用
shutdownNow()
方法后,线程池从 RUNNING 或 SHUTDOWN 状态转换为 STOP 状态。 - 当所有任务被中断后,线程池会转换为 TIDYING 状态。
- 调用
3.4 TIDYING
- 状态值:
TIDYING = 010
- 描述:所有任务已经终止,线程池即将执行终止操作。
- 状态转换:
- 当线程池从 SHUTDOWN 或 STOP 状态转换为 TIDYING 状态后,会调用
terminated()
方法。 - 调用
terminated()
方法后,线程池会转换为 TERMINATED 状态。
- 当线程池从 SHUTDOWN 或 STOP 状态转换为 TIDYING 状态后,会调用
3.5 TERMINATED
- 状态值:
TERMINATED = 011
- 描述:线程池已经完全终止。
- 状态转换:
- 调用
terminated()
方法后,线程池从 TIDYING 状态转换为 TERMINATED 状态。
- 调用
4. 线程池状态转换图
以下是线程池状态的转换关系:
RUNNING -> SHUTDOWN -> TIDYING -> TERMINATED
RUNNING -> STOP -> TIDYING -> TERMINATED
- RUNNING -> SHUTDOWN:调用
shutdown()
方法。 - RUNNING -> STOP:调用
shutdownNow()
方法。 - SHUTDOWN -> TIDYING:队列中的任务处理完成。
- STOP -> TIDYING:所有任务被中断。
- TIDYING -> TERMINATED:调用
terminated()
方法。
5. 总结
- 线程池是 Java 并发编程中的重要工具,可以有效地管理线程资源。
- 判断线程池任务是否完成可以通过
isTerminated()
、awaitTermination()
或Future.isDone()
方法。 - 线程池的状态包括 RUNNING、SHUTDOWN、STOP、TIDYING 和 TERMINATED,它们之间通过调用不同的方法进行转换。
6. 面试回答建议
在面试中回答这个问题时,可以按照以下思路:
- 介绍线程池的作用和核心参数。
- 详细说明如何判断线程池任务是否完成。
- 分析线程池的状态及其转换关系。
- 结合实际项目经验,谈谈你是否使用过线程池以及如何管理线程池的状态。
这样回答既展示了你的技术深度,也体现了你对并发编程的理解。