The difference between Executor, ExecutorService and Executors (transfer)

Original source: http://www.importnew.com/24923.html

java.util.concurrent.Executor, java.util.concurrent.ExecutorService, java.util.concurrent. Executors These three are all part of the Java Executor framework and are used to provide thread pool functions. Because creating and managing threads is very tiring, and the operating system usually limits the number of threads, it is recommended to use a thread pool to execute tasks concurrently instead of creating a thread every time a request comes in. Using the thread pool can not only improve the response time of the application, but also avoid errors such as "java.lang.OutOfMemoryError: unable to create new native thread".

In Java 1.5, developers need to care about the creation and management of thread pools, but after Java 1.5, the Executor framework provides a variety of built-in thread pools, for example: FixedThreadPool (contains a fixed number of threads), CachedThreadPool (can create new ones as needed Thread) and so on.

Executor

The main difference between Executor, ExecutorService, and Executors is that Executor is an abstract core interface (the approximate code is as follows).

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

Unlike the java.lang.Thread class that couples tasks and execution together, Executor separates the task itself from the execution task. You can read Difference between Thread and Executor to learn more about the differences between Thread and Executor.

ExecutorService

The ExecutorService interface extends the Executor interface and provides methods for returning the Future object, terminating, and closing the thread pool. When the shutDown method is called, the thread pool will stop accepting new tasks, but will complete the pending tasks.

The Future object provides asynchronous execution, which means that there is no need to wait for the completion of the task execution. Just submit the task that needs to be executed, and then check if the Future has a result when needed. If the task has been executed, you can use Future.get() The method gets the execution result. It should be noted that the Future.get() method is a blocking method. If the task is not completed when it is called, it will wait until the task execution ends.

The execution of the task can also be cancelled through the Future object returned by the ExecutorService.submit() method. Future provides the cancel() method to cancel the execution of pending tasks.

Part of the code of ExecutorService is as follows:

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 is a tool class, similar to Collections. Provide factory methods to create different types of thread pools, such as FixedThreadPool or CachedThreadPool.

Executors part of the code:

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>());
        }
}

Let's take a look at the differences between the three in detail:

Executor vs ExecutorService vs Executors

As mentioned above, these three are all part of the Executor framework. It is necessary for Java developers to learn and understand them in order to use the different types of thread pools provided by Java more efficiently. Summarize the differences between these three so that everyone can better understand:

  • The main difference between the Executor and ExecutorService interfaces is that the ExecutorService interface inherits the Executor interface and is a sub-interface of Executor
  • The second difference between Executor and ExecutorService is that the Executor interface defines the execute() method to receive a Runnable interface object, while the submit() method in the ExecutorService interface can accept Runnable and Callable interface objects.
  • The third difference between the Executor and ExecutorService interfaces is that the execute() method in Executor does not return any result, while the submit() method in ExecutorService can return the result of the operation through a Future object.
  • The fourth difference between the Executor and ExecutorService interfaces is that in addition to allowing the client to submit a task, ExecutorService also provides methods to control the thread pool. For example: call the shutDown() method to terminate the thread pool. You can learn more about closing the thread pool and how to handle pending tasks through the book " Java Concurrency in Practice ".
  • The Executors class provides factory methods to create different types of thread pools. For example: newSingleThreadExecutor() creates a thread pool with only one thread, newFixedThreadPool(int numOfThreads) creates a thread pool with a fixed number of threads, newCachedThreadPool() can create new threads as needed, but if existing threads are idle, they will be reused. There are threads.

to sum up

The following table lists the difference between Executor and ExecutorService:

Translator's Note

I personally feel that it is very convenient to use the factory method provided by the Executors class to create a thread pool, but it is not suitable for scenarios where some parameters of the thread pool need to be customized according to the actual situation.

For example:
when the threads in the thread pool are in a working state, and the number of threads has reached the maximum number of threads allowed by the thread pool, the specified saturation strategy will be adopted to process the newly submitted tasks. There are four strategies in total:

  • AbortPolicy: throw exception directly
  • CallerRunsPolicy: Use the caller's thread to run tasks
  • DiscardOldestPolicy: Discard the most recent task in the thread queue and execute the newly submitted task
  • DiscardPolicy directly discards new tasks

If you use the thread pool created by the factory method of Executors, the saturation strategy is the default AbortPolicy, so if we want to use the caller’s thread to run the task when the thread pool is full, we must create the thread pool ourselves and specify Want the saturation strategy instead of using Executors anymore.

So we can create a ThreadPoolExecutor (implementation class of the ExecutorService interface) object as needed and customize some parameters instead of calling the factory method of Executors.

Of course, in projects that use the Spring framework, you can also use the ThreadPoolTaskExecutor class provided by Spring to create a thread pool. Similar to ThreadPoolExecutor, ThreadPoolTaskExecutor also provides many parameters to customize the thread pool, such as: core thread pool size, maximum number of thread pools, saturation strategy, thread activity retention time, etc.

Guess you like

Origin blog.csdn.net/u013821237/article/details/89553610