文章目录
-
- 前言
- 线程池的基本概念
-
- 1.什么是线程池?
- [2. 线程池的主要组件](#2. 线程池的主要组件)
- [Java 线程池的实现类](#Java 线程池的实现类)
- 1、ThreadPoolExecutor
- [2、Executors 工具类](#2、Executors 工具类)
- 线程池的配置与调优
-
- 1、核心线程数和最大线程数
- 2、工作队
- [3、 拒绝策略](#3、 拒绝策略)
- 实战案例
-
- [案例 1:批量处理文件上传](#案例 1:批量处理文件上传)
- [案例 2:定时任务调度](#案例 2:定时任务调度)
- [案例 3:异步任务处理](#案例 3:异步任务处理)
- 总结
前言
线程池是 Java 并发编程中的一个重要概念,它通过复用已创建的线程来减少线程创建和销毁的开销,提高应用程序的性能。本文将详细介绍 Java 线程池的工作原理、常用配置,并结合实战案例展示其应用。
线程池的基本概念
1.什么是线程池?
线程池是一种多线程处理形式,处理过程中将任务添加到队列中,并在线程空闲时执行任务。线程池的核心思想是预先创建并维护一定数量的线程,避免频繁创建和销毁线程带来的资源消耗。
2. 线程池的主要组件
核心线程数(Core Pool Size):线程池中保持的最小线程数,即使这些线程处于空闲状态。
最大线程数(Maximum Pool Size):线程池中允许的最大线程数。
工作队列(Work Queue):用于保存等待执行的任务队列。
线程工厂(Thread Factory):用于创建新线程的工厂类,默认使用 Executors.defaultThreadFactory()。
拒绝策略(Rejected Execution Handler):当线程池无法处理新任务时的处理策略。
Java 线程池的实现类
Java 提供了多种线程池实现类,最常用的是 ThreadPoolExecutor 和 ScheduledThreadPoolExecutor。此外,Executors 工具类提供了便捷的静态方法来创建不同类型的线程池。
1、ThreadPoolExecutor
ThreadPoolExecutor 是 Java 中最常用的线程池实现类,提供了丰富的配置选项。
构造函数
bash
public ThreadPoolExecutor(
int corePoolSize, //核心线程数
int maximumPoolSize, //最大线程数
long keepAliveTime, //线程空闲时间,超过此时间的空闲线程将被终止
TimeUnit unit, //keepAliveTime 的时间单位
BlockingQueue<Runnable> workQueue, //任务队列
ThreadFactory threadFactory, //线程工厂
RejectedExecutionHandler handler) //拒绝策略
2、Executors 工具类
Executors 提供了一些静态方法来简化线程池的创建:
newFixedThreadPool(int nThreads):创建一个固定大小的线程池。
newCachedThreadPool():创建一个根据需要创建新线程的线程池。
newSingleThreadExecutor():创建一个单线程的线程池。
newScheduledThreadPool(int corePoolSize):创建一个支持定时和周期性任务执行的线程池。
线程池的配置与调优
1、核心线程数和最大线程数
核心线程数:应根据 CPU 核心数和任务类型设置。对于 CPU 密集型任务,核心线程数可以设置为 CPU 核心数;对于 I/O 密集型任务,可以适当增加。
最大线程数:应根据系统资源和任务负载动态调整,避免过多线程导致上下文切换频繁。
2、工作队
常见的工作队列类型包括:
SynchronousQueue:不存储元素的阻塞队列,直接将任务交给线程处理。
LinkedBlockingQueue:基于链表的有界或无界阻塞队列。
ArrayBlockingQueue:基于数组的有界阻塞队列。
PriorityBlockingQueue:支持优先级排序的无界阻塞队列。
3、 拒绝策略
常见的拒绝策略包括:
AbortPolicy:抛出 RejectedExecutionException 异常。
CallerRunsPolicy:由调用线程执行任务。
DiscardPolicy:直接丢弃任务。
DiscardOldestPolicy:丢弃队列中最旧的任务,然后尝试重新提交当前任务。
实战案例
案例 1:批量处理文件上传
假设我们有一个 Web 应用程序,用户可以批量上传多个文件。为了提高处理速度,我们可以使用线程池来并发处理文件上传请求。
示例代码
java
/**
* FileUploadService
* @author senfel
* @version 1.0
* @date 2025/1/3 14:52
*/
@Service
public class FileUploadService {
private final ExecutorService executor = new ThreadPoolExecutor(
4, // 核心线程数
10, // 最大线程数
60L, TimeUnit.SECONDS, // 空闲线程存活时间
new LinkedBlockingQueue<>(100), // 工作队列
Executors.defaultThreadFactory(), // 线程工厂
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
/**
* uploadFiles
* @param files
* @author senfel
* @date 2025/1/3 14:54
* @return void
*/
public void uploadFiles(List<File> files) {
for (File file : files) {
executor.submit(() -> processFile(file));
}
}
private void processFile(File file) {
try {
// 模拟文件处理逻辑
System.out.println("Processing file: " + file.getName());
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
/**
* shutdown
* @author senfel
* @date 2025/1/3 14:54
* @return void
*/
public void shutdown() {
executor.shutdown();
try {
if (!executor.awaitTermination(800, TimeUnit.MILLISECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
}
}
}
案例 2:定时任务调度
假设我们需要定期清理过期的日志文件。可以使用 ScheduledThreadPoolExecutor 来实现定时任务调度。
示例代码
java
/**
* LogCleanupService
* @author senfel
* @version 1.0
* @date 2025/1/3 14:55
*/
@Service
public class LogCleanupService {
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
/**
* startLogCleanup
* @author senfel
* @date 2025/1/3 14:56
* @return void
*/
public void startLogCleanup() {
Runnable cleanupTask = () -> {
try {
// 模拟日志清理逻辑
System.out.println("Cleaning up logs...");
Thread.sleep(5000); // 模拟耗时操作
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
};
// 每隔 1 小时执行一次日志清理任务
scheduler.scheduleAtFixedRate(cleanupTask, 0, 1, TimeUnit.HOURS);
}
/**
* shutdown
* @author senfel
* @date 2025/1/3 14:57
* @return void
*/
public void shutdown() {
scheduler.shutdown();
try {
if (!scheduler.awaitTermination(800, TimeUnit.MILLISECONDS)) {
scheduler.shutdownNow();
}
} catch (InterruptedException e) {
scheduler.shutdownNow();
}
}
}
案例 3:异步任务处理
假设我们有一个订单处理系统,需要异步处理订单通知。可以使用 CompletableFuture 结合线程池来实现异步任务处理。
示例代码
java
/**
* OrderNotificationService
* @author senfel
* @version 1.0
* @date 2025/1/3 14:57
*/
@Service
public class OrderNotificationService {
private final ExecutorService executor = Executors.newFixedThreadPool(5);
/**
* sendNotification
* @param orderId
* @author senfel
* @date 2025/1/3 14:59
* @return java.util.concurrent.CompletableFuture<java.lang.Void>
*/
public CompletableFuture<Void> sendNotification(Long orderId) {
return CompletableFuture.runAsync(() -> {
try {
// 模拟发送通知逻辑
System.out.println("Sending notification for order: " + orderId);
Thread.sleep(2000); // 模拟耗时操作
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, executor);
}
/**
* shutdown
* @author senfel
* @date 2025/1/3 14:59
* @return void
*/
public void shutdown() {
executor.shutdown();
try {
if (!executor.awaitTermination(800, TimeUnit.MILLISECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
}
}
}
总结
方案 | 优点 | 确定 | 适用场景 |
---|---|---|---|
ThreadPoolExecutor | 配置灵活,功能强大; | 需要手动管理线程池生命周期 | 适用于复杂任务调度 |
Executors | 工具类使用简单,快速创建线程池; | 默认配置可能不适合所有场景 | 适用于快速开发 |
ScheduledThreadPoolExecutor | 支持定时和周期性任务; | 功能相对单一 | 适用于定时任务调度 |
CompletableFuture | 异步编程模型,易于组合; | 学习曲线较陡 | 适用于异步任务处理 |
在实际的编码场景中,我们通过合理配置和使用线程池,可以显著提升 Java 应用程序的性能和响应速度。