1. Overview
1.概述
Multithreading and parallel processing are crucial concepts in modern application development. In Java, the Executor framework provides a way to manage and control the execution of concurrent tasks efficiently. The ExecutorService interface is at the core of this framework, and it provides two commonly used methods for submitting tasks to be executed: submit() and execute().
多线程和并行处理是现代应用程序开发中的重要概念。在 Java 中,执行器框架提供了一种高效管理和控制并发任务执行的方法。ExecutorService接口是该框架的核心,它提供了两种常用的方法来提交要执行的任务:submit()和execute()。
In this article, we’ll explore the key differences between these two methods. We’ll look at both submit() and execute() using a simple example where we want to simulate a task of calculating the sum of numbers in an array using a thread pool.
在本文中,我们将探讨这两种方法之间的主要区别。我们将通过一个简单的示例来了解 submit() 和 execute() 这两种方法,我们希望使用 线程池来模拟计算数组中数字之和的任务。
2. Usage of ExecutorService.submit( )
2.ExecutorService.submit( ) 的用法
Let’s start with the submit() method first which is widely used in the ExecutorService interface. It allows us to submit tasks for execution and returns a Future object that represents the result of the computation.
让我们先从 ExecutorService 接口中广泛使用的submit()方法开始。它允许我们提交任务以供执行,并返回一个Future对象,该对象代表计算的结果。
This Future then allows us to obtain the result of the computation, handle exceptions that occur during task execution, and monitor the status of the task. We can call get() in the Future to retrieve the result or exceptions.
然后,该 Future 允许我们获取计算结果、处理任务执行过程中出现的异常并监控任务的状态。我们可以在 Future 中调用 get() 来获取结果或异常。
Let’s start by initializing the ExecutorService:
让我们从初始化 ExecutorService 开始:
ExecutorService executorService = Executors.newFixedThreadPool(2);
Here, we’re initializing the ExecutorService with a fixed thread pool of size 2. This creates a thread pool that utilizes a fixed number of threads operating off a shared unbounded queue. In our case, at any point, at most two threads will be active processing tasks. If more tasks are sent while all existing tasks are being processed, they will be held in the queue until a processing thread becomes free.
在此,我们使用大小为 2 的固定线程池初始化 ExecutorService 。这将创建一个线程池,利用固定数量的线程在共享的 无界队列上运行。在我们的例子中,任何时候都最多有两个线程在处理任务。如果在处理所有现有任务时发送了更多任务,这些任务将被保留在队列中,直到有处理线程空闲为止。
Next, let’s create a task using Callable:
接下来,让我们使用 Callable 创建一个任务:
Callable<Integer> task = () -> {
int[] numbers = {1, 2, 3, 4, 5};
int sum = 0;
for (int num : numbers) {
sum += num;
}
return sum;
};
Importantly, here, the Callable object represents the task that returns a result and may throw an exception. In this case, it represents a task that another thread can execute and return a result or may throw exceptions. This Callable calculates the sum of integers in an array and returns the result.
重要的是,在这里,Callable对象代表返回结果并可能抛出异常的任务。在这种情况下,它代表另一个线程可以执行并返回结果或可能抛出异常的任务。此 Callable 计算数组中的整数之和并返回结果。
Now that we’ve defined the task as Callable, let’s submit this task to the ExecutorService:
现在我们已将任务定义为 可调用,让我们将该任务提交给 ExecutorService :
Future<Integer> result = executorService.submit(task);
Simply put, the submit() method takes the Callable task and submits it for execution by the ExecutorService. It returns a Future<Integer> object that represents the future result of the computation. Overall, executorService.submit() is a way to asynchronously execute a Callable task using ExecutorService allowing for concurrent execution of tasks and obtaining their results via the returned Future
简单地说,submit() 方法接收 Callable 任务并将其提交给 ExecutorService 执行。它返回一个 Future<Integer> 对象,该对象代表计算的未来结果。总的来说, executorService.submit() 是一种使用 ExecutorService 异步执行 Callable 任务的方法,允许并发执行任务,并通过返回的 Future 获得结果。
Finally, let’s check the result:
最后,让我们检查一下结果:
try {
int sum = result.get();
System.out.println("Sum calculated using submit:" + sum);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
Here, get() retrieves the result of the task execution. In this case, it fetches the sum calculated by the task and prints it. However, it’s important to note that the get() method is a blocking call and waits if the result isn’t available yet, potentially causing the thread to pause until the result is ready. It can also throw exceptions like InterruptedException or ExecutionException if the computation encounters issues while running.
在这里, get() 会获取任务执行的结果。在这种情况下,它会获取任务计算的总和并打印出来。不过,值得注意的是, get() 方法是一个阻塞调用,如果结果尚未可用,它就会等待,从而可能导致线程暂停,直到结果准备就绪。如果计算在运行过程中遇到问题,它还会抛出异常,如InterruptedException 或 ExecutionException。
Finally, let’s shut down the ExecutorService:
最后,让我们关闭 ExecutorService:
executorService.shutdown();
This shuts down the ExecutorService after the task has completed execution and releases any resources used by the service.
这将在任务执行完毕后关闭 ExecutorService 并释放服务使用的所有资源。
3. Usage of ExecutorService.execute( )
3. ExecutorService.execute( ) 的用法
The execute() method is a simpler method, defined in the Executor interface which is a parent interface of ExecutorService. It’s used to submit tasks for execution but doesn’t return a Future. This means that we cannot obtain the result of the task or handle exceptions directly through the Future object.
execute() 方法是一个更简单的方法,定义在 Executor 接口中,该接口是 ExecutorService 的父接口。它用于提交任务以供执行,但不会返回Future。 这意味着我们无法通过 Future 对象直接获取任务结果或处理异常。
It’s suitable for scenarios where we don’t need to wait for the result of the task and we don’t expect any exceptions. These tasks are executed for their side effect.
它适用于我们不需要等待任务结果,也不期望出现任何异常的情况。执行这些任务是为了它们的副作用。
Like before, we’ll create an ExecutorService with a fixed thread pool of 2. However, we’ll create a task as Runnable instead:
与之前一样,我们将创建一个具有固定线程池 2 的 ExecutorService 。 不过,我们将以 Runnable 的形式创建一个任务:
Runnable task = () -> {
int[] numbers = {1, 2, 3, 4, 5};
int sum = 0;
for (int num : numbers) {
sum += num;
}
System.out.println("Sum calculated using execute: " + sum);
};
Importantly, the task doesn’t return any result; it simply calculates the sum and prints it inside the task. We’ll now submit the Runnable task to the ExecutorService:
重要的是,该任务不会返回任何结果;它只是计算总和并在任务中打印出来。现在,我们将把 Runnable 任务提交给 ExecutorService:
executorService.execute(task);
This remains an asynchronous operation, indicating that one of the threads from the thread pool executes the task.
这仍然是一个异步操作,表示由线程池中的一个线程执行任务。
4. Conclusion
4.结论
In this article, we looked at the salient features and usages of submit() and execute() from the ExecutorService interface. In summary, both of these methods offer a way to submit tasks for concurrent execution, but they differ in their handling of task results and exceptions.
在本文中,我们介绍了来自 ExecutorService 接口的submit()和d execute() 的显著特征和用法。总之,这两种方法都提供了一种提交任务以供并发执行的方法,但它们在处理任务结果和异常方面有所不同。
The choice between submit() and execute() depends on specific requirements. If we need to obtain the result of a task or handle exceptions, we should use submit(). On the other hand, if we have a task that doesn’t return a result and we want to fire it and forget it, execute() is the right choice.
在 submit() 和 execute() 之间做出选择取决于具体要求。如果我们需要获取任务结果或处理异常,我们应该使用 submit()。另一方面,如果我们有一个不返回结果的任务,而且我们想启动它并忘记它,execute()就是正确的选择。
Moreover, if we’re working with a more complex use case and need the flexibility to manage tasks and retrieve results or exceptions, submit() is the better choice.
此外,如果我们正在处理一个更复杂的用例,并且需要灵活地管理任务和检索结果或异常, submit() 是更好的选择。
As always, the complete code for this article is available over on GitHub.
与往常一样,本文的完整代码可在 GitHub 上获取。