Java中Executor和Executors的区别小结
作者:Flying_Fish_Xuan
在Java并发编程中,Executor
和Executors
是两个密切相关但功能不同的类或接口,它们都与线程池管理和任务执行相关。理解这两者的区别对正确使用Java并发API非常重要。
1. Executor 的定义与功能
1.1 Executor 接口
Executor
是 Java 并发框架中的一个核心接口,它提供了一种将任务的提交与任务的执行解耦的机制。换句话说,Executor
接口的设计目标是将“任务的执行”这一行为抽象出来,使得任务的提交者不必关心任务是如何执行的(如是否在新的线程中执行、是否在某个线程池中执行等)。
public interface Executor { void execute(Runnable command); }
execute(Runnable command)
方法:这是 Executor
接口中唯一的方法,它接受一个实现了 Runnable
接口的任务,并安排该任务的执行。具体如何执行这个任务,由实现 Executor
接口的类决定。
1.2 Executor 接口的设计目的
Executor
接口的主要设计目的是简化并发任务的执行过程,提供了一种统一的方式来提交任务,而不需要开发者显式地创建和管理线程。通过这一接口,开发者可以将任务的执行策略(如线程池、异步执行等)与业务逻辑分离,使得代码更简洁、可维护性更高。
2. Executors 的定义与功能
2.1 Executors 类
Executors
是一个实用工具类,它包含了一些静态工厂方法,用于创建 Executor
、ExecutorService
、ScheduledExecutorService
等的常用实现。这些实现通常与线程池相关,因此 Executors
类在实际开发中非常常用。
public class Executors { // 创建一个单线程执行器 public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); } // 创建一个固定线程数的线程池 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>()); } // 其他工厂方法... }
2.2 Executors 类的常用方法
Executors
类提供了多种创建线程池的方法,每种方法返回的都是 ExecutorService
的不同实现,这些实现适合不同的并发场景:
newFixedThreadPool(int nThreads)
:创建一个固定大小的线程池。这个线程池中的线程数量是固定的,无论有多少任务提交到线程池中,线程池中同时运行的线程数量不会超过指定的大小。适用于处理稳定数量的并发任务。newCachedThreadPool()
:创建一个可缓存的线程池。这个线程池会根据需要创建新线程,如果线程空闲超过60秒则会被回收,因此在大量短期异步任务的场景中非常有用。newSingleThreadExecutor()
:创建一个单线程执行器。这个执行器确保所有任务在一个线程中按顺序执行,适用于需要顺序执行任务的场景。newScheduledThreadPool(int corePoolSize)
:创建一个定时线程池。该线程池支持任务调度和周期性执行,适用于需要定期执行任务的场景。
3. Executor 与 Executors 的区别
3.1 接口与工具类的区别
Executor
是接口:Executor
是一个接口,定义了一个任务执行的标准方法execute(Runnable command)
。它提供了一个抽象层,使得任务的提交者无需关心任务是如何被执行的。Executors
是工具类:Executors
是一个工具类,提供了创建各种Executor
、ExecutorService
实现的静态工厂方法。它简化了线程池的创建过程,使得开发者能够方便地获得适合自己应用场景的线程池实现。
3.2 关注点的区别
Executor
关注的是任务的执行:Executor
关注如何执行提交的任务,定义了任务执行的标准接口。它是一种行为规范,使得不同的执行器可以被替换而不改变代码的行为。Executors
关注的是如何创建Executor
:Executors
关注的是如何创建符合特定需求的线程池或任务执行器。它提供了多种预定义的Executor
和ExecutorService
实现,帮助开发者根据不同的并发需求选择合适的执行策略。
3.3 使用场景的区别
Executor
的使用场景:Executor
通常用于需要自定义任务执行逻辑的场景,例如自定义任务调度策略、管理任务队列等。开发者可以实现Executor
接口,定义自己的任务执行器。Executors
的使用场景:Executors
通常用于创建标准的线程池或执行器,适合常见的并发任务执行需求。通过使用Executors
提供的工厂方法,开发者可以快速创建和使用线程池,而无需关心底层实现细节。
4. 实际使用中的示例
4.1 使用 Executor
假设我们有一个简单的任务调度系统,我们可以使用 Executor
接口来抽象任务的执行过程:
public class SimpleExecutor implements Executor { @Override public void execute(Runnable command) { new Thread(command).start(); } }
这个 SimpleExecutor
类实现了 Executor
接口,它在每次接收到任务时都会创建一个新线程来执行任务。这种实现非常简单,但在实际应用中通常会使用更复杂的执行器,例如线程池。
4.2 使用 Executors
通过 Executors
工具类,我们可以很方便地创建一个固定大小的线程池,并提交任务:
public class ExecutorExample { public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(5); for (int i = 0; i < 10; i++) { executor.execute(new RunnableTask(i)); } executor.shutdown(); } } class RunnableTask implements Runnable { private int taskId; public RunnableTask(int id) { this.taskId = id; } @Override public void run() { System.out.println("Executing task " + taskId + " by " + Thread.currentThread().getName()); } }
在这个示例中,我们使用 Executors.newFixedThreadPool(5)
创建了一个固定大小为5的线程池,然后提交了10个任务给线程池执行。线程池将自动管理这些任务的执行,并复用线程来处理任务。
5. 总结
在Java并发编程中,Executor
和Executors
虽然名称相似,但它们有着截然不同的职责和用途:
Executor
是一个接口,定义了任务执行的标准方法,它是并发编程中任务执行的核心抽象。Executor
让任务的提交者不需要关心任务的具体执行方式,从而实现任务执行与业务逻辑的解耦。Executors
是一个工具类,提供了多种工厂方法来创建不同类型的Executor
和ExecutorService
实现。通过使用Executors
提供的工厂方法,开发者可以轻松创建和管理线程池,以适应各种并发编程需求。
到此这篇关于Java中Executor和Executors的区别小结的文章就介绍到这了,更多相关Java Executor Executors内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!