线程池旨在通过复用一组线程来执行多项任务,以提高性能、提高资源利用率并简化线程管理。Java 中的线程池通常由 ThreadPoolExecutor
类实现。下面我将简要描述线程池的任务执行机制。
核心组成
ThreadPoolExecutor 是 Java 并发包中的核心类,其任务执行机制包括以下几个核心组成部分:
- 核心线程 (Core Threads):在核心线程池大小(
corePoolSize
)范围内,线程池会优先启动核心线程执行任务,核心线程会始终保持存活,除非设置了allowCoreThreadTimeOut
。 - 最大线程 (Maximum Threads):当任务队列满了且当前核心线程已用尽,线程池可以创建超出的线程,但最大不超过
maximumPoolSize
。 - 任务队列 (Work Queue):用于保存等待执行的任务。常见的队列有
LinkedBlockingQueue
、ArrayBlockingQueue
、SynchronousQueue
等。 - 拒绝策略 (RejectedExecutionHandler):当任务无法执行时,会触发拒绝策略,处理新提交的任务。
执行机制
线程池的任务执行机制可以总结为以下几个步骤:
-
提交任务: 任务被提交到线程池。
threadPoolExecutor.submit(task);
-
判断核心线程:
- 如果当前运行的线程数量小于
corePoolSize
,即使有空闲线程,也会新建一个核心线程来执行任务。
- 如果当前运行的线程数量小于
-
任务入队列:
- 如果当前运行的线程数量等于或超过
corePoolSize
,则将任务加入到任务队列workQueue
。
- 如果当前运行的线程数量等于或超过
-
判断最大线程和任务队列:
- 如果任务队列已满且当前运行的线程数量小于
maximumPoolSize
,仍会新建线程来处理任务。 - 如果任务队列已满且当前运行的线程数量大于或等于
maximumPoolSize
,将触发拒绝策略handler
。
- 如果任务队列已满且当前运行的线程数量小于
-
执行任务:
- 空闲线程从队列中获取任务并执行。
- 核心线程会一直存活并处理任务,即使处于空闲状态。
- 非核心线程在执行完任务且超过
keepAliveTime
的空闲时间后会被终止。
状态转换
线程池的状态转换主要涉及以下几个状态:
- RUNNING:接受新任务和处理队列中的任务。
- SHUTDOWN:不接受新任务,但会处理队列中的任务。
- STOP:不接受新任务且不处理队列中的任务,并且会中断正在进行的任务。
- TIDYING:所有任务终止,
workerCount
为 0,执行钩子方法terminated
。 - TERMINATED:
terminated
方法执行完毕状态。
详细示例
以下是一个简单示例,展示了线程池如何处理任务:
import java.util.concurrent.*;
public class ThreadPoolMechanismExample {
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);
RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();
// 创建线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue,
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();
}
}
}
小结
- 当任务提交给线程池时,线程池首先检查当前运行的线程数,如果少于
corePoolSize
,则创建新线程处理任务。 - 如果运行的线程已达
corePoolSize
,任务会被放入任务队列workQueue
。 - 如果任务队列满了且线程数目少于
maximumPoolSize
,则会创建新的线程处理任务。 - 如果任务队列满了且线程数目已达
maximumPoolSize
,则会根据指定的拒绝策略处理新任务。
理解线程池的任务执行机制对高效地使用线程池类至关重要,有助于在并发编程中获得更好的性能和资源管理效果。