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

 在现代多核处理器时代,线程池成为了并发编程中不可或缺的工具,它不仅提高了程序性能,还简化了线程管理。线程池允许我们重用有限数量的线程来执行大量任务,从而减少了线程创建和销毁的开销。Java 中的ExecutorService接口及其实现类,如FixedThreadPool、SingleThreadExecutor、CachedThreadPool和ScheduledThreadPool,提供了强大的线程池管理功能。这些线程池通过智能地调度任务和复用线程,帮助我们优化资源利用,提高响应速度,并处理复杂的并发场景。对于 Java 开发者而言,理解线程池的工作原理和正确选择适当的线程池类型对于构建高效、可伸缩的并发应用至关重要。
肖哥弹架构 跟大家“弹弹” 高并发锁, 关注公号回复 'mvcc' 获得手写数据库事务代码
欢迎 点赞,关注,评论。
关注公号 Solomon 肖哥弹架构获取更多精彩内容
历史热点文章
1、线程池工作流程
 ExecutorService:这是线程池的管理接口,负责提交任务和管理工作线程。
任务队列(Task Queue) :这是一个先进先出(FIFO)的队列,用于存储待执行的任务。
线程池(Thread Pool) :这是一组工作线程的集合,它们从任务队列中取出任务并执行。
工作线程(Worker Thread) :线程池中的每个线程都会循环地从任务队列中取出任务并执行。
任务(Task) :这是需要执行的具体任务,可以是
Runnable或Callable对象。返回结果(Return Result) :任务执行完成后,会返回结果或异常信息。
2、ExecutorService 设计本质
线程生命周期管理:
在 Java 5 之前,开发者需要手动管理线程的创建和销毁,这不仅增加了编程复杂性,还可能导致资源浪费和系统开销。
ExecutorService通过提供线程池管理功能,简化了线程的生命周期管理。系统开销降低:
频繁地创建和销毁线程会导致性能问题和资源消耗。
ExecutorService允许线程池重用线程,从而降低了系统开销。资源利用率提升:
通过线程池复用线程,
ExecutorService提高了资源利用率和程序响应速度,使得多线程编程更加灵活和高效。丰富的任务调度和并发控制:
ExecutorService提供了丰富的任务调度和并发控制能力,使得多线程编程更加灵活和高效。硬件发展推动:
随着多核架构的出现,Java 的设计者们决定重新修订 Java 的内存模型,并在 JDK1.5 中引入了
java.util.concurrent包,其中就包括了ExecutorService接口,以支持更高效的并行计算。简化并发编程:
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 可以用来处理客户端的请求。服务器可以创建一个固定大小的线程池来同时处理多个请求,提高响应速度和吞吐量。
6.1.2. 批量数据处理
在处理批量数据时,如文件处理或数据库批量操作,ThreadPoolExecutor 可以用来并行处理数据,提高处理速度。
6.1.3. 异步任务执行
在需要异步执行任务的场景中,ThreadPoolExecutor 可以用来提交任务并在未来某个时刻获取结果。
6.1.4. 定时和周期性任务
ThreadPoolExecutor 可以与 ScheduledExecutorService 结合使用,来执行定时和周期性任务。
6.1.5. 资源受限环境下的任务处理
在资源受限的环境中,如移动设备或嵌入式系统,ThreadPoolExecutor 可以用来合理分配有限的计算资源。
7、ScheduledExecutorService 线程池设计
 ScheduledExecutorService:这是线程池的管理接口,负责提交和管理任务。
任务队列(Task Queue) :这是一个延迟队列,用于存储待执行的任务,按照预定的执行时间排序。
核心线程池(Core Thread Pool) :线程池中的核心线程会不断地从任务队列中取出任务并执行。
工作线程(Worker Thread) :线程池中的线程负责执行任务。
执行周期性任务(scheduleAtFixedRate) :用于安排任务以固定频率执行。
执行延迟任务(scheduleWithFixedDelay) :用于安排任务在每次执行完毕后按照固定延迟执行。
执行单次任务(schedule) :用于安排任务在指定延迟后执行一次。
任务完成:任务执行完毕后,如果是周期性任务,会重新调度下一次执行。
线程池关闭(Thread Pool Shutdown) :当不再需要线程池时,可以关闭线程池,等待所有任务完成或尝试立即停止所有任务。
7.1ScheduledExecutorService应用案例
7.1.1. 一次性延迟执行任务
在这个例子中,我们创建了一个 ScheduledExecutorService 实例,并安排了一个任务在延迟一定时间后执行。
7.1.2. 固定速率周期执行任务
在这个例子中,我们安排了一个任务以固定的速率周期性执行。
7.1.3. 固定延迟周期执行任务
在这个例子中,我们安排了一个任务在每次执行完毕后,等待固定的延迟时间再执行下一次。
版权声明: 本文为 InfoQ 作者【肖哥弹架构】的原创文章。
原文链接:【http://xie.infoq.cn/article/9d4402cdf1043222f963aec1d】。文章转载请联系作者。







    


评论