目录
四 ScheduledThreadPoolExecutor详解
(1) ScheduledThreadPoolExecutor 继承关系
3 scheduleAtFixedRate vs scheduleWithFixedDelay
(1) Fork/Join 与 ThreadPoolExecutor 区别
一 Executor 介绍
在现代计算机系统中,程序的执行通常需要多个任务同时进行,尤其是在多核处理器的环境下,高效地利用计算资源变得尤为重要。Java 提供了丰富的并发工具,而 Executor 框架正是其中专门用于管理和优化线程执行的核心组件。它不仅简化了线程的创建和管理,还提供了一种更为灵活和高效的方式来处理并发任务。
1. Executor 框架的作用
Executor 框架的主要作用是解耦任务提交与任务执行的过程。传统的线程管理方式通常依赖于手动创建线程并调用 start() 方法启动任务,这种方式不仅增加了代码的复杂性,还可能导致资源管理问题,如线程的频繁创建和销毁带来的性能开销,以及难以控制的并发执行数量。而 Executor 框架提供了一种更为系统化的方式,通过线程池的概念,控制任务的调度、执行和管理,使得开发人员可以专注于任务逻辑,而无需关心底层线程的管理细节。
在 Executor 框架的作用下,任务的提交与执行被分离,开发者可以使用统一的接口提交任务,而框架本身决定如何执行这些任务。这种机制带来了更高的灵活性,使得线程管理更加智能化,同时也提供了更好的可扩展性,适用于不同规模的并发场景。
2. Java 线程池的功能
线程池是 Executor 框架的重要组成部分,其核心功能在于维护一组可复用的线程,并提供任务的调度和管理。相比于传统的手动线程管理方式,线程池能够有效地控制线程的数量,避免资源浪费,并减少频繁创建和销毁线程所带来的开销。
-
线程池的主要功能
-
线程复用:线程池会维护一定数量的线程,当有新的任务提交时,会尝试使用已有的空闲线程执行任务,而不是每次都创建新线程,从而减少线程创建和销毁的开销。
-
任务队列管理:线程池内部通常会维护一个任务队列,在所有工作线程忙碌时,新提交的任务会暂存在队列中,等待线程可用时再执行。
-
线程生命周期管理:线程池会自动管理线程的生命周期,包括线程的创建、执行、空闲回收等,确保系统资源的高效利用。
-
并发控制:线程池提供了多种策略来控制任务的执行方式,如最大线程数、任务队列长度、拒绝策略等,确保系统在高负载情况下仍然能够稳定运行。
-
调度执行:除了普通的任务执行外,某些线程池还支持任务的定时执行和周期性调度,使得并发任务的调度更加灵活。
-
-
线程池的核心原理
-
线程池组成
- 核心线程数:始终存活的线程数量。
- 最大线程数:线程池可创建的最大线程数。
- 任务队列:存储待执行任务,减少线程创建。
- 线程工厂:自定义线程创建。
- 拒绝策略:超出容量时的任务处理方式。
-
任务执行流程
- 任务提交,优先使用空闲核心线程。
- 核心线程满时,任务入队列等待。
- 队列满时,创建新线程(不超过最大线程数)。
- 超限时,触发拒绝策略(丢弃、异常或由主线程执行)。
-
线程管理
- 空闲线程超过存活时间自动回收(核心线程可选回收)。
- 任务调度采用 FIFO/LIFO 队列,保证并发安全。
-
性能优化
- 合理配置线程数:避免资源浪费或 CPU 过载。
- 选择合适队列:有界队列防 OOM,无界队列适合短任务。
- 优化拒绝策略:防止任务堆积影响系统稳定性。
-
-
线程池的使用步骤
-
选择线程池类型:根据需求选择合适的线程池,如固定大小 (
FixedThreadPool)、缓存 (CachedThreadPool)、单线程 (SingleThreadExecutor)、定时任务 (ScheduledThreadPool) 等。 -
创建线程池:使用
Executors或ThreadPoolExecutor创建线程池实例。 -
提交任务:通过
execute()提交Runnable任务,或使用submit()提交Callable任务以获取返回值。 -
监控线程池:获取活跃线程数、任务队列大小等信息,优化线程池配置。
-
关闭线程池:调用
shutdown()等待任务完成后关闭,或shutdownNow()立即终止任务。 -
处理任务结果(可选):对于
Callable任务,可使用Future.get()获取执行结果。
-
3. Executor 框架与线程池的关系
在 Java 并发体系中,Executor 框架与线程池密切相关,可以说线程池是 Executor 框架最核心的实现方式之一。Executor 作为一个高级抽象,定义了任务执行的标准接口,而线程池则是具体的实现,它通过线程复用、任务队列管理和线程生命周期控制等机制,提供了一种高效的并发执行方式。
从架构上来看,Executor 框架提供了一系列接口和实现类,其中最核心的组件包括:
- Executor 接口:定义了任务执行的基本方法,所有的任务执行器都需要实现该接口。
- ExecutorService 接口:在
Executor的基础上增加了更丰富的管理功能,如提交任务、获取执行结果、关闭线程池等。 - ThreadPoolExecutor 类:
ExecutorService的主要实现类,它提供了线程池的核心功能,如线程管理、任务调度、拒绝策略等。 - ScheduledThreadPoolExecutor 类:提供了定时任务执行和周期任务调度的能力,是
ExecutorService的扩展实现。
可以看出,Executor 作为一个框架,为不同类型的任务执行需求提供了统一的接口,而线程池作为核心实现,承担了实际的任务调度和线程管理工作。这种设计不仅提高了系统的灵活性,还让开发者能够根据具体需求选择合适的线程池类型,从而实现更高效的并发处理。
4. Executor 框架的优势
Executor 框架的出现,为 Java 并发编程带来了诸多优势,主要体现在以下几个方面:
- 降低开发复杂度:开发者不需要手动管理线程的创建和销毁,而是通过提供的接口提交任务,由框架负责调度和管理,大大简化了并发程序的编写。
- 提高资源利用率:通过线程池机制,避免了线程的频繁创建和销毁,降低了 CPU 和内存的消耗,提高了系统资源的利用率。
- 提升系统稳定性:
Executor框架内置了任务队列和线程管理策略,能够有效控制并发任务的执行,防止系统因线程过多而崩溃。 - 提供灵活的扩展性:开发者可以根据具体需求,自定义线程池的参数,如核心线程数、最大线程数、任务队列类型等,使得线程管理更加可控。
- 支持任务调度:除了普通的线程池,
Executor还提供了ScheduledThreadPoolExecutor,支持任务的定时执行和周期性调度,适用于定时任务管理。
5. 适用场景
Executor 框架的应用场景非常广泛,几乎所有涉及多线程并发处理的系统都可以使用它来优化性能。以下是几个典型的应用场景:
- Web 服务器和应用服务器:在 Web 应用中,服务器需要处理大量并发请求,如果每个请求都创建一个新线程,系统很快会因为线程数过多而崩溃。使用线程池可以有效控制并发数量,提高系统的吞吐量和稳定性。
- 批量任务处理:在数据处理、日志分析、文件处理等场景中,通常需要对大量任务进行并发执行,
Executor框架能够高效管理这些任务,提高执行效率。 - 异步任务执行:在某些场景下,程序需要异步执行一些耗时任务,如网络请求、数据库查询等,使用
Executor框架可以避免阻塞主线程,提高应用的响应速度。 - 定时任务调度:在定时任务管理场景,如定期清理缓存、定时数据同步等,可以使用
ScheduledThreadPoolExecutor进行周期性调度。 - 计算密集型任务:在科学计算、人工智能等高计算需求的场景下,可以使用
Fork/Join框架进行任务拆分,提高计算效率。
二 Executor架构
1. Executor框架概述
Executor 是 Java 并发包(java.util.concurrent)提供的任务执行框架,旨在 解耦任务提交与执行,提高线程管理的灵活性,避免手动创建和销毁线程的高昂开销。它提供了一套 标准化的线程池实现,用于 任务调度、线程复用和负载管理,适用于高并发环境。
Executor 框架的核心组件主要围绕 接口层、抽象层和具体实现层 进行设计,形成一套 层次化的线程管理体系。

2. Executor框架架构
Executor 框架的架构从上到下可分为 顶层接口、抽象实现和具体实现,主要包括以下核心部分:
(1)顶层接口
-
Executor(执行器)- 核心接口,定义任务执行的基本方法
execute(Runnable command)。 - 代表任务执行的抽象概念,屏蔽底层线程管理细节。
- 核心接口,定义任务执行的基本方法
-
ExecutorService(扩展执行器)- 继承
Executor,提供更丰富的线程管理功能,如 任务提交 (submit())、任务取消 (shutdown()/shutdownNow())、批量任务执行 (invokeAll()/invokeAny()) 等。
- 继承
-
ScheduledExecutorService(定时任务执行器)- 继承
ExecutorService,支持 定时执行 和 周期性执行任务(schedule()、scheduleAtFixedRate()、scheduleWithFixedDelay())。
- 继承
(2)抽象实现
-
AbstractExecutorService(抽象执行器服务)- 提供
ExecutorService的部分默认实现,简化线程池扩展。 - 主要封装
submit()方法,使Runnable任务适配Callable。
- 提供
(3)具体实现
-
ThreadPoolExecutor(核心线程池实现)-
最核心的线程池实现,支持灵活的线程池参数配置(线程数、队列、拒绝策略等)。
-
采用 任务队列 + 线程管理 结合的方式,提高任务吞吐量和资源利用率。
-
-
ScheduledThreadPoolExecutor(定时任务线程池)- 继承
ThreadPoolExecutor,扩展 定时任务和周期任务调度能力,用于实现 定时任务、延迟任务、周期任务 等功能。
- 继承
-
ForkJoinPool(Fork/Join 框架)-
适用于 大规模并行计算,基于 工作窃取(Work Stealing) 算法优化任务拆分与合并。
-
继承
AbstractExecutorService,可用于递归拆分任务(RecursiveTask和RecursiveAction)。
-
-
Executors(工具类)提供静态方法创建各种 常用线程池,包括:
-
newFixedThreadPool(n):固定大小线程池,适用于任务量稳定的场景。 -
newCachedThreadPool():缓存线程池,适用于短期大量任务的场景。 -
newSingleThreadExecutor():单线程池,确保任务按顺序执行。 -
newScheduledThreadPool(n):定时任务线程池,支持周期性任务执行。
-
Executor 框架通过分层架构 (顶层接口 → 抽象实现 → 具体实现) 提供强大的线程管理能力,使 Java 并发编程更加灵活高效。在 java.util.concurrent 包下,它涵盖 线程池管理、任务调度、并行计算 等多个方面,极大简化了线程管理的复杂性。
三 ThreadPoolExecutor 详解
1. 线程池的使用方法
ThreadPoolExecutor 是 Java 中常用的线程池实现,用于管理和调度线程执行任务。下面通过代码示例来介绍如何使用 ThreadPoolExecutor。
(1)普通使用示例
import java.util.concurrent.*;
public class ThreadPoolExample {
public static void main(String[] args) {
// 创建一个固定大小的线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, // 核心线程数
10, // 最大线程数
60L, // 空闲线程存活时间
TimeUnit.SECONDS, // 空闲时间单位
new LinkedBlockingQueue<>(), // 任务队列
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
// 提交任务
for (int i = 0; i < 20; i++) {
executor.submit(new Task(i));
}
// 关闭线程池
executor.shutdown();
}
// 定义任务
static class Task implements Runnable {
private final int id;
public Task(int id) {
this.id = id;
}
@Override
public void run() {
System.out.println("任务 " + id + " 被执行, 线程名: " + Thread.currentThread().getName());
}
}
}
- 这个示例中,我们创建了一个具有 核心线程数 5 和 最大线程数 10 的线程池,使用
LinkedBlockingQueue作为任务队列,采用CallerRunsPolicy作为拒绝策略。 - 通过
submit()方法提交任务。线程池会在有空闲线程时执行任务,若线程池中没有空闲线程且队列已满,将根据拒绝策略处理新任务。
(2)设置线程池的各个参数示例
import java.util.concurrent.*;
public class ThreadPoolConfigExample {
public static void main(String[] args) {
// 设置线程池参数
int corePoolSize = 4; // 核心线程数
int maximumPoolSize = 8; // 最大线程数
long keepAliveTime = 60L; // 空闲线程存活时间
TimeUnit timeUnit = TimeUnit.SECONDS; // 时间单位
// 使用有界队列(ArrayBlockingQueue)限制队列大小
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(10);
// 设置线程池工厂,可以自定义线程创建方式
ThreadFactory threadFactory = Executors.defaultThreadFactory();
// 设置拒绝策略(当线程池和队列都满时的处理方式)
RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();
// 创建线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
timeUnit,
workQueue,
threadFactory,
handler
);
// 提交任务
for (int i = 0; i < 20; i++) {
executor.submit(new Task(i));
}
// 关闭线程池
executor.shutdown();
}
// 定义任务
static class Task implements Runnable {
private final int id;
public Task(int id) {
this.id = id;
}
@Override
public void run() {
System.out.println("任务 " + id + " 被执行, 线程名: " + Thread.currentThread().getName());
}
}
}
这个示例演示了如何通过 ThreadPoolExecutor 提供的构造函数自定义线程池的各个参数,包括 核心线程数、最大线程数、空闲线程存活时间、任务队列、线程工厂 和 拒绝策略。
(3)线程池关闭
executor.shutdown(); // 优雅关闭,线程池会等待任务执行完再关闭
// 或者立即停止所有线程池中的任务
executor.shutdownNow(); // 立即停止所有任务,返回待执行的任务列表
2. ThreadPoolExecutor 的实现原理
ThreadPoolExecutor 的设计思想是任务提交与线程管理的分离,通过线程池的合理配置和线程复用,减少了频繁创建和销毁线程的开销,提高了并发性能。下面是 ThreadPoolExecutor 的主要实现原理。
(1)线程池的任务执行流程
-
任务提交:
- 任务通过
submit()或execute()提交给线程池。 ThreadPoolExecutor首先判断当前线程池中是否有足够的空闲线程来执行任务。
- 任务通过
-
线程创建与执行:
- 如果当前的线程数小于
corePoolSize,则直接创建新线程执行任务。 - 如果线程池中的线程数已经达到
corePoolSize,任务将会被 放入队列(workQueue) 等待执行。 - 如果队列已满并且线程数未达到
maximumPoolSize,会继续创建新线程来处理任务。 - 如果线程数已达到
maximumPoolSize,且队列已满,线程池会根据设置的 拒绝策略(RejectedExecutionHandler) 处理任务。
- 如果当前的线程数小于
-
线程回收:
- 如果线程池中的线程数超过
corePoolSize,而且线程空闲时间超过keepAliveTime,这些线程会被销毁。 - 如果设置了
allowCoreThreadTimeOut(true),即使是核心线程也可以被回收。
- 如果线程池中的线程数超过
-
任务队列的管理:
BlockingQueue存储待执行的任务,常用的队列类型有:LinkedBlockingQueue:无界队列,适用于任务数目较大但执行时间较长的场景。ArrayBlockingQueue:有界队列,适用于任务数目有限但执行时间短的场景。SynchronousQueue:每次提交任务都需要立刻有线程来执行,适用于每个任务都必须由一个新的线程来处理的场景。
-
拒绝策略:
- 如果线程池无法处理新任务,会通过拒绝策略来处理。常见的拒绝策略有:
AbortPolicy:抛出RejectedExecutionException异常。CallerRunsPolicy:调用者线程执行任务。DiscardPolicy:丢弃任务。DiscardOldestPolicy:丢弃队列中最旧的任务,尝试提交新的任务。
- 如果线程池无法处理新任务,会通过拒绝策略来处理。常见的拒绝策略有:
(2)核心参数的动态调整
-
线程池的核心线程数 (
corePoolSize) 和最大线程数 (maximumPoolSize) 是可以动态调整的。通过setCorePoolSize(int)和setMaximumPoolSize(int)方法,可以在运行时调整线程池的规模。 -
线程池的最大线程数的作用是限制线程池中线程的总数,防止线程数过多占用系统资源。
(3)线程池的关闭与回收
-
线程池的关闭有两种方式:
shutdown():不再接受新任务,等待已提交的任务完成后关闭线程池。shutdownNow():立即停止所有任务,并返回未执行的任务列表。
-
线程池的回收通过定时检查空闲线程来进行,如果线程池中的线程数超过
corePoolSize且空闲时间超过keepAliveTime,就会销毁多余的线程。
ThreadPoolExecutor 是 Java 并发编程中非常重要的组件,它有效地管理线程的创建、执行与回收,保证了线程池的高效运行。通过合理配置线程池的参数,能够适应不同类型的任务需求,提高程序的并发能力。
四 ScheduledThreadPoolExecutor详解
ScheduledThreadPoolExecutor 是 Java 并发包(java.util.concurrent)中的一个线程池实现,它主要用于定时任务调度,支持 延迟执行 和 周期性执行 任务。它比 Timer 更加强大,支持更灵活的调度策略,并且能更好地处理异常情况。
1 使用示例
import java.util.concurrent.*;
public class ScheduledThreadPoolExecutorDemo {
public static void main(String[] args) {
// 创建一个包含 3 个线程的定时任务线程池
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(3);
// 提交一个任务,延迟 2 秒后执行
scheduler.schedule(() -> {
System.out.println("执行了一次任务: " + System.currentTimeMillis());
}, 2, TimeUnit.SECONDS);
// 提交一个任务,延迟 1 秒后开始,每 3 秒执行一次
scheduler.scheduleAtFixedRate(() -> {
System.out.println("固定速率执行任务: " + System.currentTimeMillis());
}, 1, 3, TimeUnit.SECONDS);
// 提交一个任务,延迟 1 秒后开始,每次任务执行完后等待 3 秒再执行下一次
scheduler.scheduleWithFixedDelay(() -> {
System.out.println("固定延迟执行任务: " + System.currentTimeMillis());
}, 1, 3, TimeUnit.SECONDS);
// 让主线程休眠 10 秒,观察任务执行情况
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 关闭线程池
scheduler.shutdown();
}
}
2 底层原理
(1) ScheduledThreadPoolExecutor 继承关系
它是 ThreadPoolExecutor 的子类,内部通过 任务队列 进行任务调度:
java.lang.Object
├── java.util.concurrent.AbstractExecutorService
├── java.util.concurrent.ThreadPoolExecutor
├── java.util.concurrent.ScheduledThreadPoolExecutor
(2) 核心组件
-
DelayedWorkQueue(任务队列)
ScheduledThreadPoolExecutor采用DelayedWorkQueue(基于最小堆的优先级队列) 存储任务。
任务按 执行时间(deadline)排序,最近要执行的任务排在队列头部。 -
ScheduledFutureTask(定时任务封装)
所有提交的任务都会被封装成
ScheduledFutureTask,它实现了-
RunnableScheduledFuture<V> -
RunnableFuture<V> -
Delayed -
Comparable<ScheduledFutureTask<?>>
-
-
任务调度
-
调用
schedule()提交任务任务被封装成
ScheduledFutureTask,并存入DelayedWorkQueue -
线程池从队列取出最近需要执行的任务
如果任务的执行时间没到,就阻塞等待 -
任务到期后线程池执行
如果是周期性任务(scheduleAtFixedRate/scheduleWithFixedDelay),会重新计算下次执行时间并重新加入队列
-
3 scheduleAtFixedRate vs scheduleWithFixedDelay
| 方法 | 任务调度策略 | 适用场景 |
|---|---|---|
scheduleAtFixedRate | 任务 固定间隔执行(任务执行时间不影响间隔) | 适用于定时任务,确保固定速率执行,如 心跳检测 |
scheduleWithFixedDelay | 任务 执行完毕后等待固定时间再执行 | 适用于依赖前一次执行结果的任务,如 日志归档 |
对比
scheduler.scheduleAtFixedRate(() -> {
System.out.println("scheduleAtFixedRate 执行: " + System.currentTimeMillis());
}, 1, 3, TimeUnit.SECONDS);
scheduler.scheduleWithFixedDelay(() -> {
System.out.println("scheduleWithFixedDelay 执行: " + System.currentTimeMillis());
}, 1, 3, TimeUnit.SECONDS);
scheduleAtFixedRate任务即使超时,也不会影响间隔scheduleWithFixedDelay任务如果超时,会影响下次执行时间
4 异常处理
如果 scheduleAtFixedRate 任务抛出异常,线程池不会捕获,后续任务也不会执行:
scheduler.scheduleAtFixedRate(() -> {
System.out.println("执行任务...");
throw new RuntimeException("任务异常!");
}, 1, 3, TimeUnit.SECONDS);
解决方案
使用 try-catch 捕获异常:
scheduler.scheduleAtFixedRate(() -> {
try {
System.out.println("执行任务...");
throw new RuntimeException("任务异常!");
} catch (Exception e) {
System.err.println("任务执行异常: " + e.getMessage());
}
}, 1, 3, TimeUnit.SECONDS);
五 Fork/Join 框架
1 Fork/Join 框架的作用
(1) 适用场景
Fork/Join 框架主要适用于 计算密集型任务,尤其是能够拆分为多个独立子任务的场景,例如:
(1) 递归计算:如斐波那契数列、阶乘计算。
(2) 大规模数据处理:如数组求和、并行排序。
(3) 图像处理:如并行图像渲染、视频编码。
(4) 复杂递归算法优化:如深度优先搜索(DFS)。
(2) 核心组件
Fork/Join 框架的核心组成部分包括:
(1) ForkJoinPool:
- 负责管理 Fork/Join 任务的线程池,提供任务调度功能。
(2) ForkJoinTask:
- 抽象任务类,定义了
fork()和join()方法。 - 主要有两种子类:
- RecursiveTask:有返回值的任务(如数组求和)。
- RecursiveAction:无返回值的任务(如遍历操作)。
2 Fork/Join 的基本使用模式
(1) Fork/Join 的工作流程
(1) 任务拆分:调用 fork() 方法将任务拆分为多个子任务,并提交到线程池。
(2) 任务并行执行:子任务被多个线程执行。
(3) 任务合并:所有子任务完成后,调用 join() 方法合并结果。
(2) Fork/Join 代码示例
- 示例:Fork/Join 并行计算数组求和
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.ForkJoinPool;
// 任务类:继承 RecursiveTask,进行数组求和
class SumTask extends RecursiveTask<Long> {
private static final int THRESHOLD = 10; // 阈值,小于10时直接计算
private int[] arr;
private int start, end;
public SumTask(int[] arr, int start, int end) {
this.arr = arr;
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
if ((end - start) <= THRESHOLD) { // 小任务直接计算
long sum = 0;
for (int i = start; i < end; i++) sum += arr[i];
return sum;
}
// 拆分任务
int mid = (start + end) / 2;
SumTask leftTask = new SumTask(arr, start, mid);
SumTask rightTask = new SumTask(arr, mid, end);
// 并行执行
leftTask.fork();
long rightResult = rightTask.compute();
long leftResult = leftTask.join();
return leftResult + rightResult;
}
}
// 测试代码
public class ForkJoinExample {
public static void main(String[] args) {
int[] arr = new int[1000];
for (int i = 0; i < arr.length; i++) arr[i] = i;
ForkJoinPool pool = new ForkJoinPool();
SumTask task = new SumTask(arr, 0, arr.length);
long result = pool.invoke(task);
System.out.println("Sum: " + result);
}
}
- Fork/Join 代码解析
(1) 定义任务:继承 RecursiveTask<Long>,实现 compute() 方法。
(2) 任务拆分:当任务数据量大于 THRESHOLD 时,拆分为两个子任务。
(3) 并行执行:子任务分别 fork() 和 compute() 计算,并 join() 汇总结果。
(4) 使用 ForkJoinPool 执行任务。
3 Fork/Join 框架的底层实现原理
(1) 工作窃取(Work Stealing)算法
Fork/Join 采用 工作窃取算法(Work Stealing Algorithm) 提高执行效率。
(1) 每个线程 维护一个 双端队列(Deque) 存储任务。
(2) 线程优先执行自己队列的任务。
(3) 当线程空闲时,从其他线程的队列 尾部 窃取任务执行,减少线程空闲时间。
特点:
- 避免频繁创建/销毁线程,提升性能。
- 负载均衡,防止某些线程过载,其他线程空闲。
(2) Fork/Join 任务调度机制
(1) 任务拆分(Fork):主线程将任务拆分成多个子任务,放入双端队列。
(2) 任务执行:工作线程从自己的队列 头部 取任务执行。
(3) 任务窃取:空闲线程从其他线程 尾部 窃取任务。
(4) 任务合并(Join):子任务完成后,结果汇总到主任务。
(3) ForkJoinPool 线程池结构
ForkJoinPool 作为 Fork/Join 任务调度的核心,采用 分层线程管理:
(1) 主线程池:管理所有 Worker 线程。
(2) Worker 线程:每个线程维护 一个双端队列,存储任务。
(3) 任务窃取:Worker 线程会从其他线程的队列 尾部 窃取任务,均衡负载。
4 Fork/Join 和 线程池对比
(1) Fork/Join 与 ThreadPoolExecutor 区别
| 特性 | Fork/Join | 线程池(ThreadPoolExecutor) |
|---|---|---|
| 任务拆分 | 任务递归拆分 | 任务独立提交 |
| 任务执行 | 任务间并行,窃取任务 | 线程池分配任务 |
| 任务合并 | join() 合并结果 | 任务间无直接通信 |
| 适用场景 | 计算密集型任务 | IO密集型任务 |
| 性能 | 更高效(减少线程切换) | 适用于独立任务 |
(2) 适用场景对比
(1) Fork/Join 适用于:
- 计算密集型任务(如数组求和、大数据计算)。
- 递归计算(如斐波那契数列)。
- 分治算法(如归并排序、快速排序)。
(2) 线程池适用于:
- IO 密集型任务(如数据库操作、网络请求)。
- 并行执行多个独立任务(如 Web 服务器处理多个 HTTP 请求)。
974

被折叠的 条评论
为什么被折叠?



