Java 中的线程池是由 java.util.concurrent
包中的 ThreadPoolExecutor
类实现的。线程池的底层工作原理主要包括以下几个方面:
1. 线程池的状态
线程池维护着几个内部状态,这些状态决定了线程池的行为。主要有以下几种状态:
- RUNNING:线程池接受新任务,并处理阻塞队列中的任务。
- SHUTDOWN:不再接受新任务,但是会继续处理阻塞队列中的任务直到完成。
- STOP:不再接受新任务,并取消正在执行的任务,同时清空阻塞队列。
- TIDYING:所有任务完成后,线程池进入这个状态。
- TERMINATED:线程池完成所有任务,并且所有工作线程都已经终止。
线程池的状态是通过 ctl
变量来管理的,这是一个原子变量,包含了线程池的状态和线程的数量。ctl
变量的设计使用了位分割技术,其中一部分位用于表示线程池的状态,另一部分位用于表示线程的数量。
2. 线程池的组成
线程池主要由以下几个组成部分构成:
- 核心线程数 (
corePoolSize
):线程池中保持的最小线程数量。 - 最大线程数 (
maximumPoolSize
):线程池允许创建的最大线程数量。 - 空闲线程存活时间 (
keepAliveTime
):超过核心线程数的线程空闲时的存活时间。 - 任务队列 (
workQueue
):用来存储等待执行的任务的阻塞队列。 - 线程工厂 (
threadFactory
):用于创建新线程的工厂。 - 拒绝策略 (
handler
):当线程池无法接受更多任务时的处理策略。
3. 工作流程
线程池的工作流程如下:
- 提交任务:当一个任务被提交给线程池时,线程池首先检查是否有空闲线程可以直接执行任务。
- 使用核心线程:如果有空闲线程,则直接分配给任务执行。
- 添加到队列:如果没有空闲线程,且当前线程数量小于核心线程数,线程池会创建新的线程来执行任务。如果已经达到核心线程数,则将任务放入任务队列中等待执行。
- 创建非核心线程:如果队列已满,并且线程数量小于最大线程数,线程池会创建新的非核心线程来执行任务。
- 拒绝策略:如果队列已满,并且线程数量达到最大线程数,线程池会根据拒绝策略处理任务。
4. 线程的生命周期
线程的生命周期包括以下几个阶段:
- 创建:当线程池需要新的线程时,会通过线程工厂创建。
- 就绪:线程创建后处于就绪状态,等待任务执行。
- 执行任务:线程从任务队列中取出任务执行。
- 空闲:当线程完成任务后,如果空闲时间超过了存活时间,并且线程池中的线程数量大于核心线程数,则线程会被销毁。
- 销毁:线程被销毁后,线程池中线程的数量减少。
5. 线程池的关闭
线程池可以通过 shutdown()
或 shutdownNow()
方法关闭:
- shutdown():不再接受新任务,但会等待当前任务完成。
- shutdownNow():尝试取消当前正在执行的任务,并返回未执行的任务列表。
示例代码
下面是一个简单的示例,展示线程池的创建和使用:
import java.util.concurrent.*;
public class ThreadPoolExample {
public static void main(String[] args) {
// 创建线程池
ExecutorService executor = new ThreadPoolExecutor(
2, // 核心线程数
4, // 最大线程数
60L, // 空闲线程存活时间
TimeUnit.SECONDS, // 时间单位
new LinkedBlockingQueue<>(2), // 任务队列
Executors.defaultThreadFactory(), // 线程工厂
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
// 提交任务
for (int i = 0; i < 5; i++) {
final int index = i;
executor.execute(() -> {
System.out.println("Task " + index + " is running by " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
// 关闭线程池
executor.shutdown();
}
}
通过以上描述,你可以了解 Java 中线程池的底层工作原理和实现机制。