并发编程/6种线程池设计图/1大线程池标准设计与执行规范/2种线程池管理设计(全面篇)

在现代多核处理器时代,线程池成为了并发编程中不可或缺的工具,它不仅提高了程序性能,还简化了线程管理。线程池允许我们重用有限数量的线程来执行大量任务,从而减少了线程创建和销毁的开销。Java中的ExecutorService接口及其实现类,如FixedThreadPool、SingleThreadExecutor、CachedThreadPool和ScheduledThreadPool,提供了强大的线程池管理功能。这些线程池通过智能地调度任务和复用线程,帮助我们优化资源利用,提高响应速度,并处理复杂的并发场景。对于Java开发者而言,理解线程池的工作原理和正确选择适当的线程池类型对于构建高效、可伸缩的并发应用至关重要。
1、线程池工作流程

- ExecutorService:这是线程池的管理接口,负责提交任务和管理工作线程。
- 任务队列(Task Queue) :这是一个先进先出(FIFO)的队列,用于存储待执行的任务。
- 线程池(Thread Pool) :这是一组工作线程的集合,它们从任务队列中取出任务并执行。
- 工作线程(Worker Thread) :线程池中的每个线程都会循环地从任务队列中取出任务并执行。
- 任务(Task) :这是需要执行的具体任务,可以是
Runnable或Callable对象。 - 返回结果(Return Result) :任务执行完成后,会返回结果或异常信息。
2、ExecutorService 设计本质
- 线程生命周期管理:
- 在Java 5之前,开发者需要手动管理线程的创建和销毁,这不仅增加了编程复杂性,还可能导致资源浪费和系统开销。
ExecutorService通过提供线程池管理功能,简化了线程的生命周期管理。
- 在Java 5之前,开发者需要手动管理线程的创建和销毁,这不仅增加了编程复杂性,还可能导致资源浪费和系统开销。
- 系统开销降低:
- 频繁地创建和销毁线程会导致性能问题和资源消耗。
ExecutorService允许线程池重用线程,从而降低了系统开销。
- 频繁地创建和销毁线程会导致性能问题和资源消耗。
- 资源利用率提升:
- 通过线程池复用线程,
ExecutorService提高了资源利用率和程序响应速度,使得多线程编程更加灵活和高效。
- 通过线程池复用线程,
- 丰富的任务调度和并发控制:
ExecutorService提供了丰富的任务调度和并发控制能力,使得多线程编程更加灵活和高效。
- 硬件发展推动:
- 随着多核架构的出现,Java的设计者们决定重新修订Java的内存模型,并在JDK1.5中引入了
java.util.concurrent包,其中就包括了ExecutorService接口,以支持更高效的并行计算。
- 随着多核架构的出现,Java的设计者们决定重新修订Java的内存模型,并在JDK1.5中引入了
- 简化并发编程:
ExecutorService作为Java并发编程的重要工具,简化了并发编程的复杂性,使得开发者可以更容易地实现并行处理和任务调度。
- 提高程序性能:
ExecutorService通过减少线程创建和销毁的开销,提高了程序的性能和可伸缩性。
- 线程池的易用性:
Executors类提供了便捷的工厂方法来创建不同类型的线程池,使得开发者无需手动实现复杂的线程池逻辑,就可以方便地使用线程池。
3、线程池类设计

在这个类设计图中,我们有以下组件:
- ExecutorService:这是一个接口,定义了线程池管理的方法,如
submit、invokeAll、invokeAny、shutdown等。 - ThreadPoolExecutor:这是
ExecutorService的一个具体实现,提供了线程池的详细控制,如execute、submit、shutdown等。 - ScheduledExecutorService:这是
ExecutorService的一个子接口,用于延迟执行或定期执行任务。 - FutureTask:这是
Future接口的一个实现类,用于封装异步任务,并提供方法如run、get、isDone等。
4、线程池功能范围设计
4.1. 接口定义
ExecutorService 扩展了 Executor 接口,增加了提交任务后返回 Future 对象的方法,这些方法允许任务异步执行,并提供了获取任务结果的机制。
4.2. 任务提交
submit(Callable<T> task): 提交一个返回结果的任务,并返回一个Future对象。submit(Runnable task): 提交一个不返回结果的任务,并返回一个Future对象。submit(Runnable task, T result): 提交一个不返回结果的任务,并返回一个已经设置好结果的Future对象。
4.3. 批量任务执行
invokeAll(Collection<? extends Callable<T>> tasks): 提交一个任务集合,等待所有任务完成,并返回每个任务结果的列表。invokeAny(Collection<? extends Callable<T>> tasks): 提交一个任务集合,等待任意一个任务完成,并返回该任务的结果。
4.4. 线程池管理
shutdown(): 启动一次有序的关闭,执行已提交的任务,不接受新任务。shutdownNow(): 尝试停止所有正在执行的任务,并返回未执行任务的列表。awaitTermination(long timeout, TimeUnit unit): 等待直到所有任务完成或超时。
4.5. 线程生命周期
ExecutorService 允许线程的复用,减少了线程创建和销毁的开销。线程池可以根据需要创建新线程或重用空闲线程。
4.6. 线程池的可扩展性
ExecutorService 可以与不同的线程池实现一起工作,如 FixedThreadPool、CachedThreadPool、ScheduledThreadPool 等,提供了高度的可扩展性和灵活性。
4.7. 异常处理
ExecutorService 提交的任务如果抛出异常,可以通过 Future 对象的 get 方法捕获这些异常。
4.8. 结果处理
Future 对象提供了 get 方法来获取任务结果,如果任务尚未完成,get 方法会阻塞直到任务完成。
4.9. 任务取消
Future 对象提供了 cancel 方法来取消任务,可以传入一个布尔值参数来决定是否中断正在执行的任务。
4.10. 线程工厂和拒绝策略
ExecutorService 可以使用自定义的线程工厂来创建线程,以及自定义的拒绝策略来处理任务提交过多时的情况。
ExecutorService 的设计提供了一个强大的框架,用于构建并发应用程序,它简化了并发编程的复杂性,同时提供了丰富的控制和灵活的配置选项。通过 ExecutorService,开发者可以更容易地实现线程安全的异步任务执行。
5、线程池的种类
-
FixedThreadPool:
- 拥有固定数量线程的线程池,适用于负载较重的服务器。

- 拥有固定数量线程的线程池,适用于负载较重的服务器。
-
SingleThreadExecutor:
- 只有一个线程的线程池,用于顺序执行任务。

- 只有一个线程的线程池,用于顺序执行任务。
-
CachedThreadPool:
- 根据需要创建新线程的线程池,对于短生命周期的异步任务非常合适。

- 根据需要创建新线程的线程池,对于短生命周期的异步任务非常合适。
-
ScheduledThreadPool:
- 用于延迟执行或定期执行任务的线程池。

- 用于延迟执行或定期执行任务的线程池。
-
SingleThreadScheduledExecutor:
- 单个线程的变体,用于延迟或定时执行任务。

- 单个线程的变体,用于延迟或定时执行任务。
-
WorkStealingPool:
- 基于工作窃取算法的线程池,适用于并行计算。

- 基于工作窃取算法的线程池,适用于并行计算。
这些线程池都是通过Executors工具类提供的工厂方法来创建的。除了这些,开发者还可以通过直接实例化ThreadPoolExecutor类来创建自定义配置的线程池。
ExecutorService接口本身并不定义线程池的具体实现,而是提供了一组通用的接口,用于管理和执行异步任务。不同的线程池实现提供了不同的功能和性能特性,以适应不同的并发场景。
6、ThreadPoolExecutor 线程池设计
简化版

- 核心参数初始化:包括核心线程数、最大线程数、任务队列、空闲线程存活时间和线程工厂等参数的初始化。
- 任务提交到线程池:当任务被提交到线程池时,线程池会根据当前的状态和参数来决定如何处理这个任务。
- 线程获取任务并执行:如果有空闲的核心线程,它会直接执行任务;如果没有空闲的核心线程但任务队列未满,任务会被添加到任务队列中。
- 创建非核心线程执行任务:如果任务队列已满且当前线程数小于最大线程数,会创建一个新的非核心线程来执行任务。
- 拒绝策略处理任务:如果任务队列已满且线程数达到最大线程数,任务将被拒绝,线程池会根据拒绝策略处理器来处理这个任务。
- 线程池状态管理:
ThreadPoolExecutor维护一个ctl变量,用于控制线程池的状态,包括 RUNNING、SHUTDOWN、STOP、TIDYING 和 TERMINATED 等状态。
详细版

- 创建 ThreadPoolExecutor:创建一个
ThreadPoolExecutor实例,开始线程池的初始化过程。 - 核心参数初始化:初始化线程池的核心参数,包括核心线程数、最大线程数、任务队列、空闲线程存活时间和线程工厂。
- 任务提交到线程池:当任务被提交到线程池时,线程池会根据当前的状态和参数来决定如何处理这个任务。
- 线程获取任务并执行:如果有空闲的核心线程,它会直接执行任务;如果没有空闲的核心线程但任务队列未满,任务会被添加到任务队列中。
- 创建非核心线程执行任务:如果任务队列已满且当前线程数小于最大线程数,会创建一个新的非核心线程来执行任务。
- 拒绝策略处理任务:如果任务队列已满且线程数达到最大线程数,任务将被拒绝,线程池会根据拒绝策略处理器来处理这个任务。
- 线程尝试获取新任务:任务执行完毕后,线程会尝试从任务队列中获取新的任务。
- 线程销毁或等待新任务:如果任务队列空,线程会进入空闲状态,如果达到空闲线程存活时间,线程将被销毁。
- 线程池状态检查:线程池会根据其状态来决定是否停止接收新任务,是否中断运行中的任务,以及是否等待线程终止。
6.1 ThreadPoolExecutor应用
6.1.1. 服务器端处理请求
在服务器应用中,ThreadPoolExecutor 可以用来处理客户端的请求。服务器可以创建一个固定大小的线程池来同时处理多个请求,提高响应速度和吞吐量。
int corePoolSize = 10; // 核心线程数
int maximumPoolSize = 50; // 最大线程数
long keepAliveTime = 120; // 空闲线程存活时间
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(100); // 任务队列
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
unit,
workQueue
);
// 提交任务到线程池
executor.execute(new ClientRequestHandler());
6.1.2. 批量数据处理
在处理批量数据时,如文件处理或数据库批量操作,ThreadPoolExecutor 可以用来并行处理数据,提高处理速度。
List<Data> dataList = ...; // 待处理的数据列表
int threadCount = Runtime.getRuntime().availableProcessors(); // 使用可用处理器数量作为线程数
ThreadPoolExecutor executor = new ThreadPoolExecutor(
threadCount,
threadCount,
0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>()
);
dataList.forEach(data -> executor.execute(new DataProcessorTask(data)));
6.1.3. 异步任务执行
在需要异步执行任务的场景中,ThreadPoolExecutor 可以用来提交任务并在未来某个时刻获取结果。
Future<String> future = executor.submit(() -> {
// 异步执行的任务
return "任务结果";
});
// 获取异步任务的结果
String result = future.get();
6.1.4. 定时和周期性任务
ThreadPoolExecutor 可以与 ScheduledExecutorService 结合使用,来执行定时和周期性任务。
ScheduledExecutorService scheduledExecutor = new ScheduledThreadPoolExecutor(1);
scheduledExecutor.scheduleAtFixedRate(() -> {
// 定时执行的任务
}, 0, 10, TimeUnit.SECONDS);
6.1.5. 资源受限环境下的任务处理
在资源受限的环境中,如移动设备或嵌入式系统,ThreadPoolExecutor 可以用来合理分配有限的计算资源。
int maxThreads = 4; // 根据设备性能设置最大线程数
ThreadPoolExecutor executor = new ThreadPoolExecutor(
maxThreads,
maxThreads,
60L,
TimeUnit.SECONDS,
new SynchronousQueue<>()
);
7、ScheduledExecutorService 线程池设计

- ScheduledExecutorService:这是线程池的管理接口,负责提交和管理任务。
- 任务队列(Task Queue) :这是一个延迟队列,用于存储待执行的任务,按照预定的执行时间排序。
- 核心线程池(Core Thread Pool) :线程池中的核心线程会不断地从任务队列中取出任务并执行。
- 工作线程(Worker Thread) :线程池中的线程负责执行任务。
- 执行周期性任务(scheduleAtFixedRate) :用于安排任务以固定频率执行。
- 执行延迟任务(scheduleWithFixedDelay) :用于安排任务在每次执行完毕后按照固定延迟执行。
- 执行单次任务(schedule) :用于安排任务在指定延迟后执行一次。
- 任务完成:任务执行完毕后,如果是周期性任务,会重新调度下一次执行。
- 线程池关闭(Thread Pool Shutdown) :当不再需要线程池时,可以关闭线程池,等待所有任务完成或尝试立即停止所有任务。
7.1ScheduledExecutorService应用案例
7.1.1. 一次性延迟执行任务
在这个例子中,我们创建了一个 ScheduledExecutorService 实例,并安排了一个任务在延迟一定时间后执行。
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class DelayedTaskExample {
public static void main(String[] args) {
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
Runnable task = () -> System.out.println("任务在延迟后执行:" + System.currentTimeMillis());
scheduler.schedule(task, 5, TimeUnit.SECONDS); // 5秒后执行
scheduler.shutdown(); // 执行完毕后关闭调度器
}
}
7.1.2. 固定速率周期执行任务
在这个例子中,我们安排了一个任务以固定的速率周期性执行。
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class FixedRateTaskExample {
public static void main(String[] args) {
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
Runnable task = () -> System.out.println("定期任务执行时间: " + System.currentTimeMillis());
scheduler.scheduleAtFixedRate(task, 0, 10, TimeUnit.SECONDS); // 每10秒执行一次
scheduler.shutdown(); // 执行完毕后关闭调度器
}
}
7.1.3. 固定延迟周期执行任务
在这个例子中,我们安排了一个任务在每次执行完毕后,等待固定的延迟时间再执行下一次。
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class FixedDelayTaskExample {
public static void main(String[] args) {
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
Runnable task = () -> System.out.println("带有固定延迟的任务执行时间: " + System.currentTimeMillis());
scheduler.scheduleWithFixedDelay(task, 0, 15, TimeUnit.SECONDS); // 每次执行完毕后等待15秒再执行
scheduler.shutdown(); // 执行完毕后关闭调度器
}
}
- 点赞
- 收藏
- 关注作者






评论(0)