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