一、线程池的作用

线程池是Java并发编程的核心组件,主要作用包括:

  1. 降低资源消耗,通过重复利用已创建的线程减少线程创建和销毁的开销
  2. 提高响应速度,任务到达时无需等待线程创建即可执行
  3. 提高线程的可管理性,线程是稀缺资源,线程池可进行统一分配、调优和监控

二、线程池的核心参数

ThreadPoolExecutor有7个核心参数:

1
2
3
4
5
6
7
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)

参数说明:

参数 说明
corePoolSize 核心线程数,空闲时不会被回收
maximumPoolSize 最大线程数
keepAliveTime 空闲线程存活时间
workQueue 工作队列,保存等待执行的任务
threadFactory 线程工厂,用于创建线程
handler 拒绝策略

三、线程池的工作流程

执行流程:

  1. 核心线程未满时,创建新线程执行任务
  2. 核心线程已满且队列未满时,任务加入队列等待
  3. 队列已满且线程数未达最大值时,创建非核心线程执行任务
  4. 队列已满且线程数已达最大值时,执行拒绝策略
1
2
3
4
5
6
7
8
9
10
11
12
13
任务提交

核心线程数未满? → 是 → 创建核心线程执行
↓ 否
加入工作队列

队列未满? → 是 → 等待执行
↓ 否
创建非核心线程

未达最大线程数? → 是 → 执行任务
↓ 否
执行拒绝策略

四、四种常见的线程池

1. FixedThreadPool

1
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);

核心线程数和最大线程数相同,使用无界队列LinkedBlockingQueue。适用于负载较重的服务器,需要限制并发线程数的场景。队列无界可能导致OOM。

2. CachedThreadPool

1
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

核心线程数为0,最大线程数为Integer.MAX_VALUE,使用SynchronousQueue。适用于执行短期异步任务的小程序或负载较轻的服务器。线程数可能无限增长导致OOM。

3. SingleThreadExecutor

1
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

只有一个核心线程,使用无界队列LinkedBlockingQueue。适用于需要顺序执行任务的场景。队列无界可能导致OOM。

4. ScheduledThreadPool

1
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);

支持定时和周期性任务执行。适用于需要延迟执行或定期执行任务的场景。

五、拒绝策略

队列满且线程数达到最大值时触发拒绝策略:

策略 行为
AbortPolicy 抛出RejectedExecutionException异常
CallerRunsPolicy 由调用线程执行任务
DiscardPolicy 直接丢弃任务
DiscardOldestPolicy 丢弃队列中最老的任务并重新提交

六、线程池参数配置

线程数设置

CPU密集型任务:

1
线程数 = CPU核心数 + 1

IO密集型任务:

1
线程数 = CPU核心数 * (1 + 平均等待时间/平均工作时间)

混合型任务可拆分为CPU密集型和IO密集型,分别使用不同线程池。

队列选择

队列类型 特点 适用场景
ArrayBlockingQueue 有界队列 防止资源耗尽
LinkedBlockingQueue 可选有界/无界 吞吐量要求高
SynchronousQueue 不存储元素 每个任务立即执行
PriorityBlockingQueue 优先级队列 任务有优先级需求

七、线程池监控

ThreadPoolExecutor提供的监控方法:

1
2
3
4
5
6
7
8
ThreadPoolExecutor executor = new ThreadPoolExecutor(...);

int corePoolSize = executor.getCorePoolSize();
int maximumPoolSize = executor.getMaximumPoolSize();
int poolSize = executor.getPoolSize();
int activeCount = executor.getActiveCount();
long completedTaskCount = executor.getCompletedTaskCount();
int queueSize = executor.getQueue().size();

八、最佳实践

1. 避免使用Executors创建线程池

FixedThreadPool和SingleThreadExecutor使用无界队列可能导致OOM。CachedThreadPool允许创建Integer.MAX_VALUE个线程可能导致OOM。建议直接使用ThreadPoolExecutor构造函数创建,明确指定队列大小。

2. 合理配置线程数

根据任务类型设置线程数,生产环境通过压测确定最优配置。

3. 使用有界队列

防止任务堆积导致OOM,配合拒绝策略保证系统稳定性。

4. 自定义线程工厂

1
2
3
4
5
6
ThreadFactory threadFactory = r -> {
Thread thread = new Thread(r);
thread.setName("my-thread-pool-" + thread.getId());
thread.setDaemon(false);
return thread;
};

5. 选择合适的拒绝策略

关键任务使用CallerRunsPolicy保证任务不丢失,非关键任务使用DiscardPolicy或DiscardOldestPolicy。

6. 正确关闭线程池

1
2
3
executor.shutdown();
List<Runnable> unfinishedTasks = executor.shutdownNow();
executor.awaitTermination(60, TimeUnit.SECONDS);

九、应用示例

Spring Boot配置线程池

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Configuration
public class ThreadPoolConfig {

@Bean("customExecutor")
public ThreadPoolExecutor customExecutor() {
int corePoolSize = Runtime.getRuntime().availableProcessors();
int maxPoolSize = corePoolSize * 2;

return new ThreadPoolExecutor(
corePoolSize,
maxPoolSize,
60L,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(200),
new ThreadFactoryBuilder().setNameFormat("custom-pool-%d").build(),
new ThreadPoolExecutor.CallerRunsPolicy()
);
}
}

使用线程池执行异步任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Service
public class OrderService {

@Autowired
@Qualifier("customExecutor")
private ThreadPoolExecutor executor;

public void processOrder(Order order) {
saveOrder(order);

executor.execute(() -> {
sendNotification(order);
updateStatistics(order);
});
}
}

十、总结

线程池是Java并发编程的核心组件。使用线程池可以提高系统性能和响应速度,降低资源消耗,提供线程管理能力。

核心要点:

  1. 理解线程池的工作原理和参数含义
  2. 根据任务类型配置线程数
  3. 使用有界队列防止OOM
  4. 选择合适的拒绝策略
  5. 做好线程池监控和关闭