Java线程池完全指南:参数、队列、拒绝策略及实战
一、线程池核心参数及作用
线程池的核心实现类ThreadPoolExecutor的构造函数包含7个核心参数,每个参数都直接影响线程池的行为:
public ThreadPoolExecutor(
int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 非核心线程空闲存活时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 任务队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略
)
1. corePoolSize(核心线程数)
- 作用:线程池长期保持的线程数量,即使线程处于空闲状态也不会被销毁(除非设置了
allowCoreThreadTimeOut(true)) - 行为:新任务提交时,若当前线程数小于核心线程数,会直接创建新线程执行任务,而不是放入队列
2. maximumPoolSize(最大线程数)
- 作用:线程池允许创建的最大线程数量,是线程数的上限
- 行为:当队列满且核心线程都在工作时,会创建非核心线程执行任务,直到线程总数达到此值
3. keepAliveTime(空闲存活时间)
- 作用:控制非核心线程的空闲时间
- 行为:当非核心线程空闲时间超过此值,会被销毁以释放资源
- 扩展:通过
executor.allowCoreThreadTimeOut(true)可让核心线程也遵守此规则
4. unit(时间单位)
- 作用:配合
keepAliveTime使用,指定时间单位 - 可选值:
TimeUnit.NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS等
5. workQueue(任务队列)
- 作用:核心线程都忙碌时,临时存储新提交的任务
- 特性:必须是阻塞队列(
BlockingQueue),确保线程安全 - 类型:见下文详细介绍
6. threadFactory(线程工厂)
- 作用:创建新线程的工厂,用于自定义线程属性
- 用途:可设置线程名称前缀、优先级、是否为守护线程等,便于调试和监控
7. handler(拒绝策略)
- 作用:当队列满且线程数达到
maximumPoolSize时,对新提交任务的处理策略 - 内置策略:见下文详细介绍
二、线程池常用队列类型及特性
1. ArrayBlockingQueue(数组阻塞队列)
// 有界队列,容量100,非公平模式(默认)
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(100);
// 公平模式(按线程等待顺序处理)
BlockingQueue<Runnable> fairQueue = new ArrayBlockingQueue<>(100, true);
- 特性:基于数组实现的有界队列,容量固定
- 优点:内存占用可控,支持公平/非公平调度
- 缺点:数组结构导致中间元素插入删除效率低
- 适用场景:任务数量可预测,需要控制内存占用的场景
2. LinkedBlockingQueue(链表阻塞队列)
// 无界队列(默认容量为Integer.MAX_VALUE)
BlockingQueue<Runnable> unboundedQueue = new LinkedBlockingQueue<>();
// 有界队列,容量1000
BlockingQueue<Runnable> boundedQueue = new LinkedBlockingQueue<>(1000);
- 特性:基于链表实现,可配置为有界或无界
- 优点:插入删除效率高,并发性能好(读写分离锁)
- 缺点:无界模式可能导致OOM(内存溢出)
- 适用场景:任务数量波动大,需要高并发处理的场景
3. SynchronousQueue(同步队列)
BlockingQueue<Runnable> syncQueue = new SynchronousQueue<>();
- 特性:不存储任务的队列,每个插入操作必须等待对应的移除操作
- 优点:吞吐量极高,任务直接从生产者传递给消费者
- 缺点:无法缓冲任务,必须有线程立即处理
- 适用场景:任务需要立即处理,不允许等待的实时场景
4. PriorityBlockingQueue(优先级队列)
// 任务需实现Comparable接口
BlockingQueue<Runnable> priorityQueue = new PriorityBlockingQueue<>();
// 自定义比较器
BlockingQueue<Runnable> customQueue = new PriorityBlockingQueue<>(11, Comparator.comparingInt(Task::getPriority));
- 特性:基于二叉堆的无界队列,按优先级排序任务
- 优点:可按优先级处理任务,高优先级任务先执行
- 缺点:无界可能导致OOM,同优先级任务顺序不保证
- 适用场景:任务有明确优先级区分的场景(如VIP任务优先)
5. DelayQueue(延迟队列)
// 任务需实现Delayed接口
BlockingQueue<Delayed> delayQueue = new DelayQueue<>();
- 特性:存储带延迟时间的任务,只有延迟到期后才能被取出
- 优点:支持定时/延迟执行任务
- 缺点:无界可能导致OOM
- 适用场景:定时任务、延迟重试、缓存过期清理等
三、拒绝策略详解
当线程池的队列已满且线程数达到maximumPoolSize时,新提交的任务会触发拒绝策略。JDK内置4种拒绝策略:
1. AbortPolicy(默认策略)
RejectedExecutionHandler abortHandler = new ThreadPoolExecutor.AbortPolicy();
- 行为:直接抛出
RejectedExecutionException异常 - 适用场景:需要明确知道任务被拒绝的场景,不允许任务丢失
2. CallerRunsPolicy
RejectedExecutionHandler callerRunsHandler = new ThreadPoolExecutor.CallerRunsPolicy();
- 行为:由提交任务的线程自己执行该任务
- 优点:减缓新任务提交速度,给线程池缓冲时间
- 适用场景:任务提交频率不高,希望尽量处理所有任务的场景
3. DiscardPolicy
RejectedExecutionHandler discardHandler = new ThreadPoolExecutor.DiscardPolicy();
- 行为:直接丢弃新提交的任务,不抛出异常
- 适用场景:允许任务丢失的场景,如非核心日志收集
4. DiscardOldestPolicy
RejectedExecutionHandler discardOldestHandler = new ThreadPoolExecutor.DiscardOldestPolicy();
- 行为:丢弃队列中最旧的任务(队首任务),尝试提交新任务
- 适用场景:希望新任务能被处理,旧任务可丢弃的场景
5. 自定义拒绝策略
RejectedExecutionHandler customHandler = new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// 自定义处理逻辑,如:
// 1. 将任务持久化到数据库或消息队列
// 2. 记录日志并告警
// 3. 降级处理(执行简化版任务)
System.err.println("任务" + r + "被拒绝,当前队列大小:" + executor.getQueue().size());
}
};
四、线程池完整使用示例
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadPoolCompleteExample {
public static void main(String[] args) {
// 1. 自定义线程工厂
ThreadFactory threadFactory = new ThreadFactory() {
private final AtomicInteger threadNum = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, "business-thread-" + threadNum.getAndIncrement());
thread.setDaemon(false); // 非守护线程
thread.setPriority(Thread.NORM_PRIORITY);
return thread;
}
};
// 2. 创建任务队列(有界数组队列)
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(50);
// 3. 定义拒绝策略
RejectedExecutionHandler rejectHandler = new ThreadPoolExecutor.CallerRunsPolicy();
// 4. 创建线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
3, // 核心线程数:3
5, // 最大线程数:5
30, // 空闲时间:30秒
TimeUnit.SECONDS, // 时间单位:秒
workQueue, // 任务队列
threadFactory, // 线程工厂
rejectHandler // 拒绝策略
);
// 5. 提交100个任务
for (int i = 0; i < 100; i++) {
final int taskId = i;
executor.execute(() -> {
try {
// 模拟任务执行(0.1秒)
Thread.sleep(100);
System.out.println("任务" + taskId + "由线程" +
Thread.currentThread().getName() + "完成");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
// 6. 监控线程池状态
ScheduledExecutorService monitor = Executors.newSingleThreadScheduledExecutor();
monitor.scheduleAtFixedRate(() -> {
System.out.println("\n线程池状态:" +
" 活跃线程数:" + executor.getActiveCount() +
" 队列大小:" + executor.getQueue().size() +
" 完成任务数:" + executor.getCompletedTaskCount());
}, 0, 1, TimeUnit.SECONDS);
// 7. 关闭线程池
executor.shutdown();
try {
// 等待所有任务完成,超时则强制关闭
if (!executor.awaitTermination(1, TimeUnit.MINUTES)) {
executor.shutdownNow();
}
monitor.shutdown();
} catch (InterruptedException e) {
executor.shutdownNow();
monitor.shutdownNow();
}
}
}
五、线程池适用场景
-
CPU密集型任务
- 配置:
corePoolSize = CPU核心数 + 1,maximumPoolSize = CPU核心数 + 1 - 队列:
ArrayBlockingQueue(有界,容量适中) - 示例:数据计算、图像处理
- 配置:
-
IO密集型任务
- 配置:
corePoolSize = CPU核心数 * 2,maximumPoolSize适当增大 - 队列:
LinkedBlockingQueue(可设置较大容量) - 示例:网络请求、文件IO、数据库操作
- 配置:
-
实时性要求高的任务
- 配置:
corePoolSize = 0,maximumPoolSize = Integer.MAX_VALUE - 队列:
SynchronousQueue - 示例:实时数据采集、高频交易处理
- 配置:
-
优先级任务处理
- 配置:自定义线程池,核心线程数根据需求设置
- 队列:
PriorityBlockingQueue - 示例:客服系统(VIP用户请求优先)、订单处理(加急订单优先)
-
定时/延迟任务
- 配置:使用
ScheduledThreadPoolExecutor - 队列:内部使用延迟队列
- 示例:定时备份、订单超时取消、重试机制
- 配置:使用
六、线程池使用最佳实践
-
避免使用Executors工具类
newFixedThreadPool()和newSingleThreadExecutor()使用无界队列,可能导致OOMnewCachedThreadPool()最大线程数无界,可能创建过多线程导致系统崩溃
-
合理设置核心参数
- 根据任务类型(CPU密集/IO密集)设置核心线程数
- 优先使用有界队列并合理设置容量,配合合适的拒绝策略
-
监控线程池状态
- 定期监控:活跃线程数、队列大小、完成任务数
- 关键指标异常时及时告警
-
优雅关闭线程池
- 先调用
shutdown(),给任务完成时间 - 超时后调用
shutdownNow()强制关闭 - 处理
shutdownNow()返回的未执行任务
- 先调用
-
异常处理
execute()提交的任务异常会直接抛出,需在任务内部捕获submit()提交的任务异常会被封装在Future中,需通过get()获取
通过合理配置线程池参数、选择合适的队列和拒绝策略,可以充分发挥线程池的性能优势,同时保证系统的稳定性和可维护性。
607



被折叠的 条评论
为什么被折叠?



