线程池-ThreadPoolExecutor
ThreadPoolExecutor
ThreadPoolExecutor 是 Java 并发包 (java.util.concurrent) 中的一个重要类,它提供了一个线程池的实现,用于高效地管理和复用线程,执行异步任务。
核心概念
ThreadPoolExecutor 通过线程池技术解决了频繁创建和销毁线程带来的性能开销问题。它维护一组工作线程,当有任务提交时,从池中分配线程执行任务,任务完成后线程返回池中等待下一个任务。
线程池状态和线程数量是用同一个变量保存的,类似读写锁的 state 高低位表示读写锁,目的是避免多个变量要多次CAS
主要构造参数
ThreadPoolExecutor 有多个构造函数,最完整的构造函数包含以下核心参数:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
- corePoolSize - 核心线程数,即使空闲也会保留的线程数量
- maximumPoolSize - 最大线程数,允许创建的最大线程数量
- keepAliveTime - 非核心线程空闲时的存活时间
- unit - keepAliveTime 的时间单位
- workQueue - 用于保存等待执行的任务的阻塞队列
- threadFactory - 用于创建新线程的工厂(可以指定线程名称,方便定位bug)
- handler - 当线程池和队列都饱和时的拒绝策略
常用队列类型
- LinkedBlockingQueue - 无界队列(如果不指定容量)
- ArrayBlockingQueue - 有界队列
- SynchronousQueue - 不存储元素的队列,每个插入操作必须等待另一个线程的移除操作
- PriorityBlockingQueue - 具有优先级的无界队列
拒绝策略
当线程池和队列都饱和时,会触发拒绝策略:
- AbortPolicy - 默认策略,抛出RejectedExecutionException
- CallerRunsPolicy - 由提交任务的线程直接执行该任务(别忘了主线程)
- DiscardPolicy - 直接丢弃任务,不做任何处理
- DiscardOldestPolicy - 丢弃队列中最旧的任务,然后尝试重新提交当前任务
工作流程
-
线程池刚创建时,内部没有线程
-
当提交新任务时:
- 如果当前线程数 < corePoolSize,创建新线程执行任务
- 如果当前线程数 ≥ corePoolSize,将任务放入工作队列
- 如果队列已满且线程数 < maximumPoolSize,创建新线程执行任务
- 如果队列已满且线程数 = maximumPoolSize,执行拒绝策略
-
当线程完成任务后:
- 它会从队列中获取下一个任务执行
- 如果无任务可执行且线程数 > corePoolSize,超过keepAliveTime后线程终止
-
当线程数量达到最大数量,队列也满时,执行拒绝策略
常用方法
// 执行 Runnable 任务(无返回值)
void execute(Runnable command);
// 提交 Callable 任务,返回 Future 可用于获取任务执行结果或取消任务
<T> Future<T> submit(Callable<T> task);
// 提交 Runnable 任务,返回 Future 可用于等待任务完成(get()返回null)或取消任务
Future<?> submit(Runnable task);
// 批量提交任务,返回所有任务的Future列表(按任务集合迭代器顺序)
// 所有任务完成后才返回,若任何任务抛出异常,对应 Future 的 get() 会抛出异常
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException;
// 批量提交任务,带超时时间,超时后未完成的任务会被取消
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException;
// 批量提交任务,返回第一个成功完成的任务结果(其他未完成的任务会被取消)
// 如果所有任务都失败,抛出 ExecutionException
<T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException;
// 批量提交任务,带超时时间,返回第一个成功完成的任务结果
// 超时后未完成的任务会被取消,若超时前没有任务完成则抛出 TimeoutException
<T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
/*
* 关闭线程池
* 1)线程池状态改为 SHUTDOWN
* 2)优雅关闭:线程池不再接受新任务,但会继续执行已提交的任务(包括队列中的任务)
* 3)不强制中断:不会主动中断正在运行的线程,而是等待它们自然完成
*/
void shutdown();
/* 关闭线程池
* 1)线程池状态改为 STOP
* 2)立即关闭:线程池不再接受新任务,并尝试中断所有正在执行的任务
* 3)清空任务队列:返回尚未执行的 Runnable 任务列表(List<Runnable>)
* 4)不保证所有任务停止:如果任务不检查中断状态(如未处理 InterruptedException),可能继续执行
*/
List<Runnable> shutdownNow();
shutdownNow() 响应中断示例
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.submit(() -> {
try {
Thread.sleep(1000);
System.out.println("Task 1 completed"); // 可能被中断
} catch (InterruptedException e) {
System.out.println("Task 1 interrupted");
Thread.currentThread().interrupt();
}
});
executor.submit(() -> System.out.println("Task 2 completed"));
List<Runnable> notExecutedTasks = executor.shutdownNow(); // 尝试中断所有任务
System.out.println("Cancelled tasks: " + notExecutedTasks.size());
优雅停止线程池示例
/**
* 优雅关闭线程池的通用方法
* @param executor 要关闭的线程池
* @param poolName 线程池名称(用于日志)
*/
public static void shutdownThreadPool(ExecutorService executor, String poolName) {
if (executor == null) {
return;
}
try {
// 第一步: 停止接收新任务
executor.shutdown();
System.out.println(poolName + ": 开始关闭,等待正在执行的任务完成...");
// 第二步: 等待一段时间让现有任务完成
if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
// 第三步: 如果超时,取消所有正在执行的任务
System.out.println(poolName + ": 等待超时,尝试强制关闭...");
List<Runnable> unfinishedTasks = executor.shutdownNow();
System.out.println(poolName + ": 已取消 " + unfinishedTasks.size() + " 个排队中的任务");
// 第四步: 再次等待一段时间
if (!executor.awaitTermination(15, TimeUnit.SECONDS)) {
System.err.println(poolName + ": 线程池未能正常终止!");
}
} else {
System.out.println(poolName + ": 所有任务已完成");
}
} catch (InterruptedException e) {
// 如果等待过程中线程被中断,立即取消所有任务
System.err.println(poolName + ": 关闭过程被中断!立即强制关闭...");
executor.shutdownNow();
// 恢复中断状态
Thread.currentThread().interrupt();
} finally {
System.out.println(poolName + ": 关闭流程完成");
}
}
Executors
newFixedThreadPool
- 固定线程数量,大小核心线程数量=最大线程数量(既然没有救急线程,存活时间也就没意义了,就是 0)
- 使用无界队列 LinkedBlockingQueue(没指定大小就是 Integer 最大值,常认为就是无界的)
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
newCachedThreadPool
- 线程全都是救急线程,核心线程数时0,最大线程数量无限(Integer 最大值)
- 存活时间是 60 秒
- 使用 SynchronousQueue 队列(SynchronousQueue 容量是 0,当有线程取数据时,才能入队成功)
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
newSingleThreadExecutor
- 单线程的线程池,核心线程数量=最大线程数量=1,也就不需要存活时间了
- 使用无界队列 LinkedBlockingQueue(没指定大小)
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
newScheduledThreadPool
ScheduledExecutorService
可调度的线程池,ScheduledExecutorService 扩展了 ExecutorService 接口,提供了定时任务和周期性任务的执行能力
- 可以在指定延迟后执行任务
- 可以固定速率或固定延迟重复执行任务
周期性任务如果抛出异常,后续执行会被取消,所以应该在任务内部捕获所有异常
常用方法
// 创建单线程的定时任务执行器
ScheduledExecutorService singleThreadScheduler = Executors.newSingleThreadScheduledExecutor();
// 创建多线程的定时任务执行器(指定线程数)
ScheduledExecutorService multiThreadScheduler = Executors.newScheduledThreadPool(4);
// 指定时间后执行一次任务(Runnable 任务,无返回值)
ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);
// 指定时间后执行一次任务(Callable 任务,有返回值)
<V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit);
// 固定频率执行任务(只是指定间隔时间,有可能间隔时间小于任务执行时间)
ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);
// 和上面的区别是,这个方法会等任务【执行完成】后等待一定的时间再次执行
ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit);
使用示例
public class SchedulerExample {
public static void main(String[] args) {
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
// 一次性任务
scheduler.schedule(() -> System.out.println("一次性任务"), 3, TimeUnit.SECONDS);
// 固定速率任务
scheduler.scheduleAtFixedRate(() ->
System.out.println("固定速率任务 - " + LocalTime.now()),
1, 2, TimeUnit.SECONDS);
// 固定延迟任务
scheduler.scheduleWithFixedDelay(() -> {
System.out.println("固定延迟任务 Start - " + LocalTime.now());
try {
Thread.sleep(1000); // 模拟任务执行时间
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("固定延迟任务 End - " + LocalTime.now());
}, 1, 3, TimeUnit.SECONDS);
// 30秒后关闭调度器
scheduler.schedule(() -> {
scheduler.shutdown();
try {
if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
scheduler.shutdownNow();
}
} catch (InterruptedException e) {
scheduler.shutdownNow();
}
}, 30, TimeUnit.SECONDS);
}
}

浙公网安备 33010602011771号