1. 线程池的执行流程和原理
线程池的执行流程:
- 提交任务:客户端通过
execute或submit方法将任务提交到线程池。 - 核心线程:如果当前运行的线程数小于核心线程数(
corePoolSize),线程池会创建新的线程来执行任务,即使其他空闲的核心线程也可以执行新任务。 - 工作队列:如果当前运行的线程数已经达到核心线程数,任务会被放入工作队列(
workQueue)中等待执行。 - 最大线程:如果工作队列已满且当前运行的线程数小于最大线程数(
maximumPoolSize),线程池会创建新的线程来执行任务。 - 拒绝策略:如果当前运行的线程数已经达到最大线程数且工作队列已满,线程池会根据拒绝策略处理新提交的任务。
线程池的原理:
- 线程复用:线程池通过复用已创建的线程来执行新任务,减少了线程创建和销毁的开销。
- 资源控制:通过设置核心线程数、最大线程数和工作队列大小,可以有效控制资源的使用。
- 任务调度:线程池可以根据任务的优先级和类型进行调度,提高系统的响应速度和吞吐量。
2. 线程池的拒绝策略
线程池的拒绝策略决定了当线程池无法接受新任务时的行为。Java 提供了以下几种拒绝策略:
- AbortPolicy:默认策略,抛出
RejectedExecutionException异常。 - CallerRunsPolicy:由调用线程(提交任务的线程)执行该任务,适用于任务不能丢弃的情况。
- DiscardPolicy:直接丢弃任务,不抛出异常。
- DiscardOldestPolicy:丢弃工作队列中最老的任务,然后尝试重新提交当前任务。
3. 如何确定线程池的核心线程数
确定线程池的核心线程数需要考虑以下几个因素:
- CPU 核心数:对于 CPU 密集型任务,核心线程数通常设置为
CPU 核心数 + 1,以充分利用 CPU 资源。 - IO 密集型任务:对于 IO 密集型任务,核心线程数可以设置为
CPU 核心数 * 2或更高,因为 IO 密集型任务大部分时间都在等待 IO 操作完成。 - 任务类型:根据任务的性质和系统负载情况,适当调整核心线程数。
- 系统资源:考虑系统内存和 CPU 资源的限制,避免过度占用资源导致系统性能下降。
4. 如何优雅的关闭线程池
优雅地关闭线程池意味着在关闭线程池之前,确保所有已提交的任务都已完成。Java 提供了以下方法来优雅地关闭线程池:
- shutdown:停止接收新任务,但允许已提交的任务继续执行,直到所有任务完成。
- awaitTermination:等待所有任务完成,超时后返回。
- shutdownNow:尝试停止所有正在执行的任务,并返回等待执行的任务列表。
5. 用 Java 实现优雅的关闭线程池
如何优雅地关闭线程池:
import java.util.concurrent.*;
public class ThreadPoolExample {
public static void main(String[] args) {
// 创建线程池
ExecutorService executorService = new ThreadPoolExecutor(
4, // 核心线程数
10, // 最大线程数
60, // 空闲线程存活时间
TimeUnit.SECONDS, // 时间单位
new LinkedBlockingQueue<>(100), // 工作队列
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
// 提交任务
for (int i = 0; i < 150; i++) {
final int taskId = i;
executorService.submit(() -> {
System.out.println("Task " + taskId + " is running by " + Thread.currentThread().getName());
try {
Thread.sleep(1000); // 模拟任务执行时间
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Task " + taskId + " is completed by " + Thread.currentThread().getName());
});
}
// 优雅地关闭线程池
shutdownAndAwaitTermination(executorService);
}
public static void shutdownAndAwaitTermination(ExecutorService pool) {
pool.shutdown(); // 不再接受新任务
try {
// 等待所有任务完成,最多等待 60 秒
if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
pool.shutdownNow(); // 尝试停止所有正在执行的任务
// 再次等待所有任务完成,最多等待 10 秒
if (!pool.awaitTermination(10, TimeUnit.SECONDS))
System.err.println("Pool did not terminate");
}
} catch (InterruptedException ie) {
pool.shutdownNow(); // (Re-)Cancel if current thread also interrupted
Thread.currentThread().interrupt(); // Preserve interrupt status
}
}
}
- 创建线程池:使用
ThreadPoolExecutor创建一个线程池,指定核心线程数、最大线程数、空闲线程存活时间、工作队列和拒绝策略。 - 提交任务:通过
executorService.submit方法提交多个任务。 - 优雅地关闭线程池:
- shutdown:停止接收新任务,但允许已提交的任务继续执行。
- awaitTermination:等待所有任务完成,超时后返回。
- shutdownNow:尝试停止所有正在执行的任务,并返回等待执行的任务列表。
通过这种方式,可以确保在关闭线程池之前,所有已提交的任务都能完成,从而实现优雅的关闭。