使用ThreadPoolTaskExecutor
时间: 2025-06-04 17:05:17 浏览: 15
<think>我们将使用Spring框架中的ThreadPoolTaskExecutor来实现多线程导出PDF的功能。这种实现方式更加适合在Spring应用环境中使用,因为它提供了更丰富的配置选项和更好的集成。步骤:1.配置ThreadPoolTaskExecutor(可以在配置类中配置)2.在导出方法中使用线程池提交任务3.等待所有任务完成注意:由于我们的任务是I/O密集型(生成PDF),所以线程池大小可以设置得大一些(比如CPU核心数*2~5),但具体需要根据实际情况调整。下面是一个完整的实现示例:</think>
以下是在 Spring Framework 中使用 `ThreadPoolTaskExecutor` 实现多线程批量导出 PDF 的解决方案:
```java
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;
public class PdfExporter {
// 配置线程池(通常在Spring配置类中初始化)
private ThreadPoolTaskExecutor taskExecutor;
public PdfExporter() {
taskExecutor = createTaskExecutor();
}
// 创建并配置线程池
private ThreadPoolTaskExecutor createTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10); // 核心线程数
executor.setMaxPoolSize(20); // 最大线程数
executor.setQueueCapacity(100); // 队列容量
executor.setThreadNamePrefix("pdf-export-");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
/**
* 批量导出PDF文档
* @param ckDocTableDataList 待导出的文档数据列表
*/
public void export(List<CkDocTableData> ckDocTableDataList) {
// 使用CountDownLatch跟踪任务完成状态
CountDownLatch latch = new CountDownLatch(ckDocTableDataList.size());
List<CompletableFuture<Void>> futures = ckDocTableDataList.stream()
.map(data -> CompletableFuture.runAsync(
() -> {
try {
generatePdf(data);
} catch (Exception e) {
handleExportException(data, e);
} finally {
latch.countDown(); // 任务完成计数器减一
}
},
taskExecutor.getThreadPoolExecutor()
))
.collect(Collectors.toList());
// 等待所有任务完成
try {
latch.await(); // 阻塞直到所有任务完成
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("导出任务被中断: " + e.getMessage());
}
// 检查是否有任务失败
long failedTasks = futures.stream().filter(f -> f.isCompletedExceptionally()).count();
if(failedTasks > 0) {
System.out.println("警告: " + failedTasks + "个文档导出失败");
}
}
// PDF生成方法(实际实现细节)
private void generatePdf(CkDocTableData data) {
System.out.println("生成PDF: " + data.getId() +
" Thread: " + Thread.currentThread().getName());
// PDF生成具体实现
}
// 异常处理方法
private void handleExportException(CkDocTableData data, Exception e) {
System.err.println("文档ID:" + data.getId() + "导出失败: " + e.getMessage());
// 可添加重试逻辑或错误记录
}
}
```
### 配置类示例(通常放在 Spring Boot 配置中):
```java
@Configuration
@EnableAsync
public class ThreadPoolConfig {
@Bean(name = "exportTaskExecutor")
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("pdf-export-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
```
### 代码解释:
1. **线程池配置详解**:
- `setCorePoolSize(10)`:线程池保持的最小线程数
- `setMaxPoolSize(20)`:线程池最大线程数(任务高峰时创建)
- `setQueueCapacity(100)`:任务队列容量(核心线程忙碌时存储任务)
- `CallerRunsPolicy`:队列满时的处理策略(让调用者线程执行任务)
- `setThreadNamePrefix`:方便调试的任务线程命名
2. **任务调度机制**:
- 使用 `CompletableFuture.runAsync()` 将每个文档导出任务提交到线程池
- `CountDownLatch` 精确跟踪所有任务的完成状态(比 `allOf()` 更高效)
- `finally` 块确保计数器无论任务成功与否都递减
3. **异常处理**:
- 每个任务独立的异常捕获和处理
- 主线程中断处理(避免无限等待)
- 任务完成后检查异常状态
4. **最佳实践**:
- 线程池定义为 Spring Bean 实现复用
- 使用 `@EnableAsync` 支持 Spring 的异步处理
- 线程名前缀便于日志分析
### 使用 Spring Boot 异步注解的替代方案:
```java
@Service
public class AsyncPdfExporter {
@Autowired
private ThreadPoolTaskExecutor exportTaskExecutor;
@Async("exportTaskExecutor")
public CompletableFuture<Void> exportDocument(CkDocTableData data) {
// PDF生成逻辑
return CompletableFuture.completedFuture(null);
}
public void batchExport(List<CkDocTableData> documents) {
List<CompletableFuture<Void>> futures = documents.stream()
.map(this::exportDocument)
.collect(Collectors.toList());
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
}
}
```
### 对比建议:
- 大量简单任务使用原生 `CompletableFuture`(本文主方案)性能更高
- 复杂任务或需要 Spring 事务管理的任务使用 `@Async` 方案更合适
阅读全文
相关推荐


















