ThreadPoolExecutor的原理?
ThreadPoolExecutor
是 Java 并发包 (java.util.concurrent
) 中最常用的实现之一,用于管理和复用一组线程以执行任务。理解 ThreadPoolExecutor
的原理有助于更好地使用和配置线程池。以下是 ThreadPoolExecutor
的核心工作原理:
1. 核心参数
要配置 ThreadPoolExecutor
,需要了解以下几个核心参数:
corePoolSize
:核心线程池大小,即线程池在没有空闲线程的情况下,创建新线程的最大数目。maximumPoolSize
:最大线程池大小,即在任务队列满的情况下,为处理任务所能创建的最大线程数。keepAliveTime
:线程空闲时间,即非核心线程在没有新任务到达后可以存活的最大时间。unit
:上述空闲时间的单位。workQueue
:任务队列,存放待执行的任务。threadFactory
:用于创建新线程。handler
:拒绝策略,当任务无法被执行时使用。
2. 工作原理
ThreadPoolExecutor
的工作原理可以总结为以下几个阶段:
提交任务
当任务被提交到线程池时,会经历以下步骤:
- 如果当前运行的线程少于
corePoolSize
,即使有空闲线程,也会新建一个线程处理任务。 - 如果当前运行的线程数量达到了
corePoolSize
,会将任务加入到任务队列workQueue
中。 - 如果任务队列已满且当前运行的线程数量小于
maximumPoolSize
,则会新建线程处理任务。 - 如果任务队列已满且当前运行的线程数量大于或等于
maximumPoolSize
,则会根据拒绝策略handler
处理新提交的任务。
执行任务
- 核心线程:始终存活,即使它们在
keepAliveTime
内空闲。 - 非核心线程:在
keepAliveTime
内空闲会被终止和回收。
示例代码
下面是一个简单的 ThreadPoolExecutor
示例:
import java.util.concurrent.*;
public class ThreadPoolExecutorExample {
public static void main(String[] args) {
// 创建线程池,有以下参数
int corePoolSize = 2;
int maximumPoolSize = 4;
long keepAliveTime = 10;
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(2);
ThreadFactory threadFactory = Executors.defaultThreadFactory();
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
ExecutorService threadPool = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue,
threadFactory,
handler);
// 提交任务
for (int i = 0; i < 10; i++) {
final int taskNumber = i;
threadPool.submit(() -> {
try {
System.out.println("Task " + taskNumber + " is running by " + Thread.currentThread().getName());
Thread.sleep(2000);
System.out.println("Task " + taskNumber + " is completed by " + Thread.currentThread().getName());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
// 关闭线程池
threadPool.shutdown();
try {
if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) {
threadPool.shutdownNow();
}
} catch (InterruptedException ex) {
threadPool.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
3. 拒绝策略
当任务无法提交到线程池时,ThreadPoolExecutor
提供了四种拒绝策略:
- AbortPolicy(默认):抛出
RejectedExecutionException
。 - CallerRunsPolicy:由调用者线程来运行此任务。
- DiscardPolicy:抛弃当前任务。
- DiscardOldestPolicy:抛弃队列中最老的任务,然后重新提交当前任务。
4. 任务队列
任务队列的选择影响到任务的执行和拒绝策略的触发时机,可以选择以下几种队列:
- 直接提交队列(SynchronousQueue):一个不存储元素的阻塞队列,每次插入操作必须等待另一个线程的移除操作。
- 有界队列(ArrayBlockingQueue):一个有界的阻塞队列,由数组构成。
- 无界队列(LinkedBlockingQueue):一个基于链接节点的无界阻塞队列。
- 优先级队列(PriorityBlockingQueue):一个具有优先级的无限阻塞队列。
5. 线程池状态
ThreadPoolExecutor
内部维护着线程池的状态:
- RUNNING:可以接受新任务并处理排队任务。
- SHUTDOWN:不接受新任务,但会处理排队的任务。
- STOP:不接受新任务,不处理排队的任务,并中断正在进行的任务。
- TIDYING:所有任务都终止,
workerCount
为 0,terminated()
方法将被调用。 - TERMINATED:
terminated()
已完成。
总结
ThreadPoolExecutor
提供了一种高效的线程管理方式,通过明确的配置参数,可以灵活地适应不同的并发场景和任务需求。了解其工作原理和使用方法,有助于在开发中更有效地利用线程资源,提高程序的性能和稳定性。