【译】Executor, ExecutorService 和 Executors 间的不同

本文深入探讨了Java中Executor框架的核心组件,包括Executor、ExecutorService和Executors的不同之处,解释了它们各自的作用以及如何高效地使用Java提供的线程池。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Executor, ExecutorService 和 Executors 间的不同

java.util.concurrent.Executorjava.util.concurrent.ExecutorServicejava.util.concurrent. Executors 这三者均是 Java Executor 框架的一部分,用来提供线程池的功能。因为创建和管理线程非常心累,并且操作系统通常对线程数有限制,所以建议使用线程池来并发执行任务,而不是每次请求进来时创建一个线程。使用线程池不仅可以提高应用的响应时间,还可以避免"java.lang.OutOfMemoryError: unable to create new native thread" 之类的错误。

在 Java 1.5 时,开发者需要关心线程池的创建和管理,但在 Java 1.5 之后 Executor 框架提供了多种内置的线程池,例如:FixedThreadPool(包含固定数目的线程),CachedThreadPool(可根据需要创建新的线程)等等。

Executor

Executor, ExecutorService, 和 Executors 最主要的区别是 Executor 是一个抽象层面的核心接口(大致代码如下)。

      
1
2
3
      
public interface Executor {
void execute(Runnable command);
}

不同于 java.lang.Thread 类将任务和执行耦合在一起, Executor 将任务本身和执行任务分离,可以阅读 difference between Thread and Executor 来了解 Thread 和 Executor 间更多的不同。

ExecutorService

ExecutorService 接口 对 Executor 接口进行了扩展,提供了返回 Future 对象,终止,关闭线程池等方法。当调用 shutDown 方法时,线程池会停止接受新的任务,但会完成正在 pending 中的任务。

Future 对象提供了异步执行,这意味着无需等待任务执行的完成,只要提交需要执行的任务,然后在需要时检查 Future 是否已经有了结果,如果任务已经执行完成,就可以通过 Future.get() 方法获得执行结果。需要注意的是,Future.get() 方法是一个阻塞式的方法,如果调用时任务还没有完成,会等待直到任务执行结束。

通过 ExecutorService.submit() 方法返回的 Future 对象,还可以取消任务的执行。Future 提供了 cancel() 方法用来取消执行 pending 中的任务。

ExecutorService 部分代码如下:

      
1
2
3
4
5
6
      
public interface ExecutorService extends Executor {
void shutdown();
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException;
}

Executors

Executors 是一个工具类,类似于 Collections。提供工厂方法来创建不同类型的线程池,比如 FixedThreadPool 或 CachedThreadPool。

Executors 部分代码:

      
1
2
3
4
5
6
7
8
9
10
      
public class Executors {
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
}
}

下面详细看一下三者的区别:

Executor vs ExecutorService vs Executors

正如上面所说,这三者均是 Executor 框架中的一部分。Java 开发者很有必要学习和理解他们,以便更高效的使用 Java 提供的不同类型的线程池。总结一下这三者间的区别,以便大家更好的理解:

  • Executor 和 ExecutorService 这两个接口主要的区别是:ExecutorService 接口继承了 Executor 接口,是 Executor 的子接口
  • Executor 和 ExecutorService 第二个区别是:Executor 接口定义了 execute()方法用来接收一个Runnable接口的对象,而 ExecutorService 接口中的 submit()方法可以接受RunnableCallable接口的对象。
  • Executor 和 ExecutorService 接口第三个区别是 Executor 中的 execute() 方法不返回任何结果,而 ExecutorService 中的 submit()方法可以通过一个 Future 对象返回运算结果。
  • Executor 和 ExecutorService 接口第四个区别是除了允许客户端提交一个任务,ExecutorService 还提供用来控制线程池的方法。比如:调用 shutDown() 方法终止线程池。可以通过 《Java Concurrency in Practice》 一书了解更多关于关闭线程池和如何处理 pending 的任务的知识。
  • Executors 类提供工厂方法用来创建不同类型的线程池。比如: newSingleThreadExecutor() 创建一个只有一个线程的线程池,newFixedThreadPool(int numOfThreads)来创建固定线程数的线程池,newCachedThreadPool()可以根据需要创建新的线程,但如果已有线程是空闲的会重用已有线程。

总结

下表列出了 Executor 和 ExecutorService 的区别:

Executor ExecutorService
Executor 是 Java 线程池的核心接口,用来并发执行提交的任务 ExecutorService 是 Executor 接口的扩展,提供了异步执行和关闭线程池的方法
提供execute()方法用来提交任务 提供submit()方法用来提交任务
execute()方法无返回值 submit()方法返回Future对象,可用来获取任务执行结果
不能取消任务 可以通过Future.cancel()取消pending中的任务
没有提供和关闭线程池有关的方法 提供了关闭线程池的方法

译者注

个人觉得,利用 Executors 类提供的工厂方法来创建一个线程池是很方便,但对于需要根据实际情况自定义线程池某些参数的场景,就不太适用了。

举个例子:
当线程池中的线程均处于工作状态,并且线程数已达线程池允许的最大线程数时,就会采取指定的饱和策略来处理新提交的任务。总共有四种策略:

  • AbortPolicy: 直接抛异常
  • CallerRunsPolicy: 用调用者的线程来运行任务
  • DiscardOldestPolicy: 丢弃线程队列里最近的一个任务,执行新提交的任务
  • DiscardPolicy 直接将新任务丢弃

如果使用 Executors 的工厂方法创建的线程池,那么饱和策略都是采用默认的 AbortPolicy,所以如果我们想当线程池已满的情况,使用调用者的线程来运行任务,就要自己创建线程池,指定想要的饱和策略,而不是使用 Executors 了。

所以我们可以根据需要创建 ThreadPoolExecutor(ExecutorService接口的实现类) 对象,自定义一些参数,而不是调用 Executors 的工厂方法创建。

当然,在使用 Spring 框架的项目中,也可以使用 Spring 提供的 ThreadPoolTaskExecutor 类来创建线程池。ThreadPoolTaskExecutor 与 ThreadPoolExecutor 类似,也提供了许多参数用来自定义线程池,比如:核心线程池大小,线程池最大数量,饱和策略,线程活动保持时间等等。

<think>我们正在讨论Java中的ExecutorServiceExecutors。根据引用内容,我们可以总结如下: ExecutorService是一个接口,它扩展了Executor接口,提供了更丰富的线程池管理功能。而Executors是一个工厂类,用于创建各种类型的ExecutorService实例(线程池)。 关系: - ExecutorService 是接口,定义了线程池的行为(如提交任务、关闭线程池等)。 - Executors 是工具类,提供了静态工厂方法来创建不同类型的ExecutorService实例。 作用: - ExecutorService:管理线程池,可以提交任务(Callable/Runnable),并获取Future对象来跟踪任务状态结果。 - Executors:简化线程池的创建过程,提供了一些预定义的线程池配置(如固定大小线程池、缓存线程池、定时任务线程池等)。 详细说明: 1. ExecutorService接口: - 继承自Executor接口(Executor只有一个execute方法,用于执行Runnable任务)。 - 增加了submit方法(可以提交Callable或Runnable任务,并返回Future对象)、shutdown方法、invokeAll等方法。 - 主要实现类有ThreadPoolExecutorScheduledThreadPoolExecutor。 2. Executors工厂类: - 提供静态方法创建不同类型的线程池(返回ExecutorService实例): - newFixedThreadPool(int nThreads):固定大小的线程池。 - newCachedThreadPool():可缓存的线程池,线程数可动态调整。 - newSingleThreadExecutor():单线程的线程池。 - newScheduledThreadPool(int corePoolSize):支持定时及周期性任务执行的线程池。 - 这些方法内部实际上是通过配置ThreadPoolExecutor或ScheduledThreadPoolExecutor的参数来创建线程池的。 使用示例(参考引用[3][4]): ```java // 使用Executors创建固定大小的线程池 ExecutorService executorService = Executors.newFixedThreadPool(5); // 提交任务 for (int i = 0; i < 10; i++) { executorService.submit(new Runnable() { public void run() { // 任务逻辑 } }); } // 关闭线程池 executorService.shutdown(); ``` 注意:引用[4]提到,在大型项目中,建议直接使用ThreadPoolExecutor等类来创建线程池,因为这样可以更精确地控制线程池的参数(如核心线程数、最大线程数、队列类型等),避免使用Executors可能导致的资源耗尽问题(比如newFixedThreadPoolnewSingleThreadExecutor使用的是无界队列,可能堆积大量请求导致OOM)。但在小型代码或学习时,使用Executors比较方便。 总结: - ExecutorService是线程池的抽象接口,定义了线程池的基本操作。 - Executors是创建ExecutorService实例的工具类,提供了便捷的工厂方法。 [^1]: 引用[1]说明了ExecutorExecutorServiceExecutors的关系:Executor是基础接口,ExecutorService扩展了它,Executors是工厂类。 [^2]: 引用[2]指出ExecutorService接口在java.util.concurrent包中,并提到了两个主要实现类。 [^3]: 引用[3]展示了使用Executors创建线程池并提交任务的基本代码。 [^4]: 引用[4]比较了使用ExecutorServiceExecutors创建线程池的适用场景。 [^5]: 引用[5]举例说明了Executor如何管理线程任务。 §§相关问题§§ 1. 为什么在大型项目中建议直接使用ThreadPoolExecutor而不是Executors? 2. ExecutorService的submitexecute方法有什么区别? 3. 如何正确关闭ExecutorService线程池?</think>### JavaExecutorServiceExecutors的关系解析 #### 一、核心概念定义 1. **ExecutorService** - 是一个**接口**(位于`java.util.concurrent`包) - 定义了线程池的核心操作方法: ```java void shutdown(); // 优雅关闭 List<Runnable> shutdownNow(); // 立即关闭 <T> Future<T> submit(Callable<T> task); // 提交任务 <T> List<Future<T>> invokeAll(Collection<Callable<T>> tasks); // 批量提交 ``` - 主要实现类:`ThreadPoolExecutor``ScheduledThreadPoolExecutor` 2. **Executors** - 是一个**工厂工具类**(提供静态方法) - 用于快速创建预配置的线程池实例: ```java // 创建固定大小线程池 ExecutorService fixedPool = Executors.newFixedThreadPool(5); // 创建可缓存线程池 ExecutorService cachedPool = Executors.newCachedThreadPool(); // 创建定时任务线程池 ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(3); ``` #### 二、核心关系图解 ```mermaid graph TD A[Executors 工厂类] -->|创建| B(ExecutorService 实例) B --> C[ThreadPoolExecutor] B --> D[ScheduledThreadPoolExecutor] E[开发者] -->|使用| A E -->|直接配置| C ``` #### 三、功能对比表 | 特性 | ExecutorService | Executors | |---------------------|-------------------------------------|------------------------------------| | **类型** | 接口 | 工具类 | | **主要作用** | 定义线程池行为规范 | 提供线程池实例的工厂方法 | | **线程池创建** | 需手动实现或通过实现类实例化 | 直接返回预配置的线程池实例 | | **配置灵活性** | 高(可精细控制所有参数) | 低(使用默认配置) | | **适用场景** | 大型项目/需要定制化线程池 | 快速原型/小型项目/简单任务 | | **典型方法** | submit(), shutdown(), invokeAll() | newFixedThreadPool(), newCachedThreadPool() | #### 四、使用场景建议 1. **优先使用Executors的情况**(引用[4]) ```java // 快速创建线程池(适合简单场景) ExecutorService executor = Executors.newFixedThreadPool(5); executor.submit(() -> System.out.println("Task executed")); ``` - 原型开发 - 简单并发任务 - 学习测试环境 2. **优先使用ExecutorService实现类的情况**(引用[4][5]) ```java // 精细控制线程池参数(适合生产环境) ExecutorService customPool = new ThreadPoolExecutor( 5, // 核心线程数 20, // 最大线程数 60L, TimeUnit.SECONDS, // 空闲线程存活时 new ArrayBlockingQueue<>(100) // 任务队列 ); ``` - 高并发生产环境 - 需要限制资源使用的场景 - 特殊队列策略需求(如优先级队列) #### 五、底层实现原理 当调用`Executors.newFixedThreadPool(5)`时: 1. 实际创建的是`ThreadPoolExecutor`实例 2. 默认使用无界队列`LinkedBlockingQueue` ```java // Executors类源码片段 public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor( nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>() // 无界队列 ); } ``` 3. 可能的风险:无界队列可能导致OOM(引用[4]建议生产环境直接使用`ThreadPoolExecutor`) #### 六、最佳实践总结 1. **学习/原型阶段**: 使用`Executors`快速创建线程池,降低学习成本[^4] 2. **生产环境**: 直接实例化`ThreadPoolExecutor`,明确配置: ```java // 安全的生产环境配置 ExecutorService safePool = new ThreadPoolExecutor( 10, 50, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1000), // 有界队列防OOM new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略 ); ``` 3. **定时任务**: 使用`ScheduledExecutorService`替代过时的`Timer`类: ```java ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2); // 每5秒执行一次 scheduler.scheduleAtFixedRate( () -> System.out.println("定时任务"), 0, 5, TimeUnit.SECONDS ); ``` > 关键结论:`ExecutorService`是线程池的行为规范接口,而`Executors`是快速创建预配置线程池的工厂工具类。两者配合使用可实现从快速开发到精细化控制的完整线程管理方案[^1][^2][^5]。 [^1]: ExecutorService接口扩展了Executor的基本行为,是线程池框架的基石 [^2]: Executors工厂类为开发者提供了创建线程池的便捷途径 [^3]: 通过submit方法提交任务到ExecutorService [^4]: 生产环境建议直接使用ThreadPoolExecutor进行精细控制 [^5]: ExecutorService负责线程管理任务执行,开发者无需关注底层细节
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值