线程池的常见代码题实现
一、是什么?
线程池是一种线程管理机制,通过预先创建一组可复用的线程,避免频繁创建/销毁线程的开销。Java中主要通过java.util.concurrent.ExecutorService
接口及其实现类(如ThreadPoolExecutor
)实现。
二、解决什么问题
- 减少资源消耗:线程创建/销毁开销大(约1ms/次)
- 控制并发数:防止无限制创建线程导致OOM
- 统一管理任务:提供任务队列、拒绝策略等机制
- 提高响应速度:任务到达时可直接使用空闲线程
三、核心方法(JDK8+)
方法 | 说明 |
---|---|
newFixedThreadPool() | 固定大小线程池 |
newCachedThreadPool() | 弹性线程池(无大小限制) |
newScheduledThreadPool() | 支持定时任务的线程池 |
submit() | 提交任务(返回Future) |
shutdown() | 优雅关闭(处理完队列任务) |
shutdownNow() | 立即关闭(返回未处理任务) |
四、应用场景
- Web服务器请求处理
- 批量数据处理(如Excel导入)
- 异步任务执行(如发送短信/邮件)
- 定时任务调度(替代Timer)
五、代码示例(含JDK8新特性)
java
import java.util.concurrent.*;
public class ThreadPoolDemo {
public static void main(String[] args) {
// 1. 创建线程池(推荐手动创建)
ExecutorService pool = new ThreadPoolExecutor(
2, // 核心线程数
5, // 最大线程数
60, // 空闲线程存活时间
TimeUnit.SECONDS, // 时间单位
new ArrayBlockingQueue<>(10), // 任务队列
Executors.defaultThreadFactory(), // 线程工厂
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);
// 2. 提交任务(使用Lambda表达式)
for (int i = 0; i < 15; i++) {
final int taskId = i;
pool.submit(() -> {
System.out.println(Thread.currentThread().getName()
+ " 执行任务-" + taskId);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
// 3. 关闭线程池(JDK8+优雅关闭)
pool.shutdown();
try {
if (!pool.awaitTermination(1, TimeUnit.MINUTES)) {
pool.shutdownNow();
}
} catch (InterruptedException e) {
pool.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
六、执行流程可视化
七、重要注意事项
- 禁止使用Executors创建线程池(易导致OOM)
newFixedThreadPool
:使用无界队列(LinkedBlockingQueue)newCachedThreadPool
:最大线程数=Integer.MAX_VALUE
- 合理配置参数:
- CPU密集型:线程数 = CPU核心数 + 1
- IO密集型:线程数 = CPU核心数 * 2
- 必须处理拒绝策略(默认AbortPolicy会抛异常)
- CallerRunsPolicy:由提交任务的线程执行
- DiscardOldestPolicy:丢弃最旧任务
- 使用ThreadLocal需谨慎:线程复用可能导致数据污染
- 优雅关闭:
shutdown()
+awaitTermination()
组合
八、JDK8+增强特性
- CompletableFuture:更强大的异步编程java
CompletableFuture.supplyAsync(() -> "result", pool) .thenApplyAsync(s -> s.toUpperCase()) .thenAccept(System.out::println);
- 并行流(Parallel Stream):底层使用ForkJoinPooljava
IntStream.range(1,100).parallel().forEach(i -> process(i));
九、常见面试题实现
- 交替打印ABC
java
ExecutorService pool = Executors.newFixedThreadPool(3);
AtomicInteger count = new AtomicInteger(0);
for (char c : "ABC".toCharArray()) {
pool.execute(() -> {
while (count.get() < 30) {
synchronized (count) {
if (count.get() % 3 == c - 'A') {
System.out.print(c);
count.incrementAndGet();
count.notifyAll();
} else {
try { count.wait(); }
catch (InterruptedException e) {}
}
}
}
});
}
pool.shutdown();
- 批量任务结果收集
java
List<Callable<String>> tasks = new ArrayList<>();
for (int i = 0; i < 10; i++) {
final int id = i;
tasks.add(() -> "Result-" + id);
}
List<Future<String>> futures = pool.invokeAll(tasks);
futures.stream().map(f -> {
try { return f.get(); }
catch (Exception e) { return null; }
}).forEach(System.out::println);
十、总结
要点 | 说明 |
---|---|
创建原则 | 使用ThreadPoolExecutor构造函数 |
参数配置 | 根据任务类型设置core/max大小 |
队列选择 | CPU密集型选SynchronousQueue,IO密集型选LinkedBlockingQueue |
关闭规范 | 必须调用shutdown(),配合awaitTermination |
新特性 | 优先使用CompletableFuture替代Future |
最佳实践:生产环境建议使用Guava的
ThreadFactoryBuilder
或Spring的ThreadPoolTaskExecutor
,提供更完善的监控和管理功能。