Java 线程池详解
·
一、线程池概述
1.1 什么是线程池
线程池是一种线程管理机制,它维护一组可重用的线程,用于执行提交的任务,避免了频繁创建和销毁线程的开销。
1.2 线程池的优势
-
降低资源消耗:复用已创建的线程,减少线程创建和销毁的开销
-
提高响应速度:任务到达时,无需等待线程创建即可立即执行
-
提高线程可管理性:统一管理、分配和监控线程
-
防止资源耗尽:通过限制线程数量,避免系统过载
二、ThreadPoolExecutor 核心类
2.1 构造函数参数详解
public ThreadPoolExecutor(
int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 空闲线程存活时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 工作队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略处理器
)
2.2 各参数详细说明
1. 核心线程数 (corePoolSize)
-
线程池长期维持的线程数
-
即使线程空闲也不会被回收(除非设置allowCoreThreadTimeOut)
2. 最大线程数 (maximumPoolSize)
-
线程池允许创建的最大线程数
-
当工作队列满时,会创建新线程直到达到此值
3. 空闲线程存活时间 (keepAliveTime)
-
当线程数大于核心线程数时,多余的空闲线程的存活时间
-
超过此时间,多余的线程会被终止
4. 工作队列 (workQueue)
-
用于存放等待执行的任务
-
常用队列类型:
// 1. 有界队列
new ArrayBlockingQueue<>(capacity);
// 2. 无界队列(慎用,可能导致OOM)
new LinkedBlockingQueue<>();
// 3. 同步移交队列(不存储元素,每个插入必须等待一个移除操作)
new SynchronousQueue<>();
// 4. 优先级队列
new PriorityBlockingQueue<>();
6. 拒绝策略 (RejectedExecutionHandler)
当线程池和工作队列都满时,对新任务的处理策略:
// 1. AbortPolicy(默认):抛出RejectedExecutionException异常
new ThreadPoolExecutor.AbortPolicy();
// 2. CallerRunsPolicy:由调用者线程执行该任务
new ThreadPoolExecutor.CallerRunsPolicy();
// 3. DiscardPolicy:直接丢弃任务,不抛异常
new ThreadPoolExecutor.DiscardPolicy();
// 4. DiscardOldestPolicy:丢弃队列中最旧的任务,然后重新尝试执行
new ThreadPoolExecutor.DiscardOldestPolicy();
// 5. 自定义拒绝策略
RejectedExecutionHandler customHandler = (runnable, executor) -> {
// 自定义处理逻辑
System.out.println("Task rejected: " + runnable);
// 可以记录日志、持久化任务等
};
三、Executors 工厂类
Executors 提供了创建常见线程池的便捷方法:
3.1 常用线程池类型
// 1. 固定大小线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
// 2. 单线程线程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
// 3. 缓存线程池(根据需要创建新线程)
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
// 4. 定时/周期线程池
ScheduledExecutorService scheduledThreadPool =
Executors.newScheduledThreadPool(5);
3.2 Executors 的潜在问题
// 可能导致OOM的风险
// 1. newFixedThreadPool 和 newSingleThreadExecutor 使用无界队列
ExecutorService executor = Executors.newFixedThreadPool(10);
// 工作队列是 LinkedBlockingQueue(默认Integer.MAX_VALUE)
// 2. newCachedThreadPool 可能创建过多线程
// 最大线程数是 Integer.MAX_VALUE
四、线程池工作流程
// 线程池执行流程图解
1. 提交任务
↓
2. 核心线程是否已满?
├─ 否:创建核心线程执行任务
↓ 是:
3. 工作队列是否已满?
├─ 否:任务加入队列等待
↓ 是:
4. 线程数是否达到最大线程数?
├─ 否:创建新线程执行任务
↓ 是:
5. 执行拒绝策略
五、线程池生命周期管理
5.1 状态转换
RUNNING → SHUTDOWN → STOP → TIDYING → TERMINATED
(运行中) (不接受新任务) (不处理任务) (整理状态) (已终止)
处理队列任务 中断所有线程
5.2 关闭方法
ExecutorService executor = Executors.newFixedThreadPool(5);
// 1. shutdown(): 平缓关闭
// - 不再接受新任务
// - 已提交的任务会继续执行
// - 不会立即终止
executor.shutdown();
// 2. shutdownNow(): 立即关闭
// - 不再接受新任务
// - 尝试中断正在执行的任务
// - 返回等待执行的任务列表
List<Runnable> notExecutedTasks = executor.shutdownNow();
// 3. awaitTermination(): 等待终止
executor.shutdown();
try {
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
六、线程池监控
6.1 监控指标
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);
// 获取监控信息
int corePoolSize = executor.getCorePoolSize(); // 核心线程数
int poolSize = executor.getPoolSize(); // 当前线程数
int activeCount = executor.getActiveCount(); // 活动线程数
long completedTaskCount = executor.getCompletedTaskCount(); // 已完成任务数
long taskCount = executor.getTaskCount(); // 总任务数
int queueSize = executor.getQueue().size(); // 队列大小
6.2 扩展 ThreadPoolExecutor 实现监控
public class MonitorThreadPoolExecutor extends ThreadPoolExecutor {
public MonitorThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
@Override
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
System.out.println("准备执行任务: " + r);
System.out.println("活动线程数: " + getActiveCount());
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
System.out.println("任务执行完成: " + r);
if (t != null) {
System.out.println("任务执行异常: " + t.getMessage());
}
}
@Override
protected void terminated() {
super.terminated();
System.out.println("线程池已终止");
}
}
七、实际应用示例
7.1 创建自定义线程池
// 创建自定义线程池的最佳实践
public class ThreadPoolManager {
private static final int CORE_POOL_SIZE = 5;
private static final int MAX_POOL_SIZE = 20;
private static final int QUEUE_CAPACITY = 100;
private static final Long KEEP_ALIVE_TIME = 1L;
public static ThreadPoolExecutor createThreadPool() {
// 自定义线程工厂
ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat("custom-pool-%d")
.setDaemon(false)
.setPriority(Thread.NORM_PRIORITY)
.build();
// 自定义拒绝策略
RejectedExecutionHandler rejectionHandler = (r, executor) -> {
// 记录日志
log.warn("Task rejected, queue is full. Task: {}", r);
// 可以在此处实现任务降级或持久化
// 例如:保存到数据库,等待后续处理
};
return new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAX_POOL_SIZE,
KEEP_ALIVE_TIME,
TimeUnit.MINUTES,
new ArrayBlockingQueue<>(QUEUE_CAPACITY),
threadFactory,
rejectionHandler
);
}
}
7.2 定时任务线程池
// 定时任务线程池示例
ScheduledExecutorService scheduledExecutor =
Executors.newScheduledThreadPool(3);
// 延迟执行
scheduledExecutor.schedule(() -> {
System.out.println("延迟5秒执行");
}, 5, TimeUnit.SECONDS);
// 固定频率执行(不受任务执行时间影响)
scheduledExecutor.scheduleAtFixedRate(() -> {
System.out.println("每3秒执行一次");
}, 1, 3, TimeUnit.SECONDS);
// 固定延迟执行(任务结束后延迟指定时间)
scheduledExecutor.scheduleWithFixedDelay(() -> {
System.out.println("任务结束延迟2秒后执行");
}, 1, 2, TimeUnit.SECONDS);
7.3 CompletableFuture 结合线程池
// 使用自定义线程池执行 CompletableFuture
ExecutorService customExecutor = Executors.newFixedThreadPool(3);
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
System.out.println("任务1在线程: " + Thread.currentThread().getName());
}, customExecutor);
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
System.out.println("任务2在线程: " + Thread.currentThread().getName());
return "Result";
}, customExecutor);
// 组合多个任务
CompletableFuture<String> combinedFuture = future1.thenCombineAsync(future2,
(v1, v2) -> "Combined: " + v2, customExecutor);
combinedFuture.thenAccept(result -> {
System.out.println("最终结果: " + result);
});
八、最佳实践和注意事项
8.1 线程池参数设置建议
// 1. CPU密集型任务
// 核心线程数 = CPU核心数 + 1
int cpuCores = Runtime.getRuntime().availableProcessors();
ThreadPoolExecutor cpuIntensivePool = new ThreadPoolExecutor(
cpuCores + 1,
cpuCores * 2,
60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1000)
);
// 2. IO密集型任务
// 核心线程数 = CPU核心数 * 2
ThreadPoolExecutor ioIntensivePool = new ThreadPoolExecutor(
cpuCores * 2,
cpuCores * 4,
60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1000)
);
// 3. 混合型任务
// 根据业务场景调整,可通过监控动态调整
8.2 常见问题及解决方案
问题1:线程泄漏
// 解决方案:正确关闭线程池
ExecutorService executor = Executors.newFixedThreadPool(5);
try {
// 执行业务逻辑
Future<?> future = executor.submit(() -> {
// 任务逻辑
});
future.get(10, TimeUnit.SECONDS); // 设置超时时间
} catch (TimeoutException e) {
// 处理超时
future.cancel(true);
} finally {
// 确保关闭
executor.shutdown();
}
问题2:任务堆积
// 解决方案:合适的队列大小和拒绝策略
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, 10, 60L, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100), // 使用有界队列
new ThreadPoolExecutor.CallerRunsPolicy() // 调用者执行策略
);
// 监控队列大小
if (executor.getQueue().size() > 80) {
log.warn("线程池队列已使用80%,当前大小: {}", executor.getQueue().size());
// 触发告警或动态调整
}
问题3:线程池死锁
// 避免场景:任务间相互等待
ExecutorService executor = Executors.newFixedThreadPool(2);
Future<?> future1 = executor.submit(() -> {
Future<?> innerFuture = executor.submit(() -> {
System.out.println("内部任务");
});
try {
innerFuture.get(); // 可能导致死锁
} catch (Exception e) {
e.printStackTrace();
}
});
九、Spring中的线程池
9.1 Spring线程池配置
@Configuration
@EnableAsync
public class ThreadPoolConfig {
@Bean("taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数
executor.setCorePoolSize(5);
// 最大线程数
executor.setMaxPoolSize(10);
// 队列容量
executor.setQueueCapacity(100);
// 线程名称前缀
executor.setThreadNamePrefix("async-thread-");
// 拒绝策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
// 等待时间
executor.setAwaitTerminationSeconds(60);
executor.initialize();
return executor;
}
}
9.2 使用 @Async 注解
@Service
public class UserService {
@Async("taskExecutor")
public CompletableFuture<User> getUserAsync(Long userId) {
// 异步执行逻辑
User user = userRepository.findById(userId);
return CompletableFuture.completedFuture(user);
}
}
十、总结
线程池选择指南
| 场景 | 推荐线程池 | 说明 |
|---|---|---|
| CPU密集型 | FixedThreadPool | 固定线程数,避免上下文切换 |
| IO密集型 | CachedThreadPool | 弹性线程池,适应IO等待 |
| 定时任务 | ScheduledThreadPool | 支持定时和周期性任务 |
| 单线程顺序执行 | SingleThreadExecutor | 保证任务顺序执行 |
| 混合型任务 | 自定义ThreadPoolExecutor | 根据业务特点调整参数 |
关键要点
-
避免使用无界队列,防止OOM
-
合理设置线程数,根据任务类型调整
-
实现合适的拒绝策略,保证系统稳定性
-
及时关闭线程池,释放资源
-
监控线程池状态,及时发现和解决问题
线程池调优建议
-
通过监控确定最佳参数
-
考虑使用动态线程池(根据负载自动调整)
-
针对不同业务使用不同的线程池
-
考虑使用线程池隔离,避免相互影响
线程池是Java并发编程的重要工具,正确使用可以显著提升系统性能和稳定性。在实际应用中,应根据具体业务场景选择合适的线程池类型和参数配置。
更多推荐



所有评论(0)