Use of ExecutorService in Java concurrency

As an asynchronous execution framework in Java, you can easily create a multithreaded execution environment with ExecutorService.

This article explains how to use ExecutorService in detail.

Create the ExecutorService

There are generally two ways to create an ExecutorService.

The first way is to use the Factory like method in Executors, for example:

ExecutorService executor = Executors.newFixedThreadPool(10);
Copy the code

In addition to the newFixedThreadPool method, Executors includes many methods for creating ExecutorService.

The second approach is to create an ExecutorService directly, because ExecutorService is an interface, and we need to instantiate an implementation of the ExecutorService.

Here we use ThreadPoolExecutor as an example:

ExecutorService executorService =
            new ThreadPoolExecutor(1.1.0L, TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue<Runnable>());
Copy the code

Assign Tasks to the ExecutorService

ExecutorService can perform both Runnable and Callable tasks. Runnable has no return value and Callable has a return value. Let’s take a look at the use of the two cases:

Runnable runnableTask = () -> {
    try {
        TimeUnit.MILLISECONDS.sleep(300);
    } catch(InterruptedException e) { e.printStackTrace(); }}; Callable<String> callableTask = () -> { TimeUnit.MILLISECONDS.sleep(300);
    return "Task's execution";
};
Copy the code

Assigning tasks to the ExecutorService can be done by calling the xecute(), submit(), invokeAny(), and invokeAll() methods.

Execute () returns void, which is used to submit a Runnable task.

executorService.execute(runnableTask);
Copy the code

Submit () returns the Future, which can submit a Runnable task or a Callable task. There are two methods for submitting Runnable:

<T> Future<T> submit(Runnable task, T result); Future<? > submit(Runnable task);Copy the code

The first method returns the result passed in. The second method returns NULL.

Take a look at the use of callable:

Future<String> future = 
  executorService.submit(callableTask);
Copy the code

InvokeAny () passes a list of tasks to the executorService and returns the result of a successful return.

String result = executorService.invokeAny(callableTasks);
Copy the code

InvokeAll () passes a list of tasks to the executorService and returns the results of all successful executions:

List<Future<String>> futures = executorService.invokeAll(callableTasks);
Copy the code

Close the ExecutorService

ExecutorService does not shut down automatically after tasks within the ExecutorService are completed. It waits to receive a new task. To shutdown the ExecutorService, we need to call the shutdown() or shutdownNow() methods.

Shutdown () immediately destroys the ExecutorService, which causes ExecutorServic to stop accepting new tasks and wait for all existing tasks to complete.

executorService.shutdown();
Copy the code

ShutdownNow () does not guarantee that all tasks have been executed and returns a list of unexecuted tasks:

List<Runnable> notExecutedTasks = executorService.shutdownNow();
Copy the code

The best shutdown method recommended by Oracle is to use with awaitTermination:

executorService.shutdown();
       try {
           if(! executorService.awaitTermination(800, TimeUnit.MILLISECONDS)) { executorService.shutdownNow(); }}catch (InterruptedException e) {
           executorService.shutdownNow();
       }
Copy the code

Stop receiving tasks and wait for a certain amount of time for all tasks to complete. If the specified time is exceeded, the task is terminated immediately.

Future

Submit () and invokeAll() both return a Future object. We’ve covered Future in detail in previous articles. Here’s just how to use it:

Future<String> future = executorService.submit(callableTask);
String result = null;
try {
   result = future.get();
} catch (InterruptedException | ExecutionException e) {
   e.printStackTrace();
}
Copy the code

ScheduledExecutorService

ScheduledExecutorService provides a mechanism for scheduling tasks.

We create ScheduledExecutorService like this:

ScheduledExecutorService executorService
                = Executors.newSingleThreadScheduledExecutor();
Copy the code

ExecutorService schedule methods can be passed to Runnable or Callable:

Future<String> future = executorService.schedule(() -> {
        // ...
        return "Hello world";
    }, 1, TimeUnit.SECONDS); ScheduledFuture<? > scheduledFuture = executorService.schedule(() -> {// ...
    }, 1, TimeUnit.SECONDS);
Copy the code

There are two similar approaches:

scheduleAtFixedRate( Runnable command, long initialDelay, long period, TimeUnit unit )

scheduleWithFixedDelay( Runnable command, long initialDelay, long delay, TimeUnit unit ) 
Copy the code

The difference between the two is that the former period is calculated by the start time of the task, while the latter period is calculated by the end time of the task.

The ExecutorService and Fork/Join

Java 7 introduced the Fork/Join framework. So what’s the difference?

ExecutorService allows users to control the generated threads themselves, providing more fine-grained control over threads. Fork/Join is used to make the task complete more quickly.

The code for this article is at github.com/ddean2009/l…

See flydean’s blog for more tutorials