Executor framework
Executor framework
Started since java 1.5
Runnable (vs) Callable comes into point when we are using Executer framework.
ExecutorService is a subinterface of Executor
interface, which accepts both Runnable and Callable tasks.
https://docs.oracle.com/javase/tutorial/essential/concurrency/exinter.html
Before java 1.5, multi-threading was implemented using Interface Runnable
. Runnable
was part of java since 1.0
The problem with Runnables
is, after the thread task is completed, we do not have a way to collect the Threads information (the results from the execution of those threads).
Callables
were introduced to solve this problem.
There are a few different ways to delegate tasks for execution to an ExecutorService.
execute(Runnable task):void
crates new thread but not blocks main thread or caller thread as this method return void.submit(Callable<?>):Future<?>
,submit(Runnable):Future<?>
crates new thread and blocks main thread when you are usingfuture.get()
java.util.concurrent package
The java.util.concurrent
package defines three executor interfaces:
Executor
, a simple interface that supports launching new tasks.ExecutorService
, a subinterface of Executor, which adds features that help manage the life cycle, both of the individual tasks and of the executor itself.ScheduledExecutorService
, a subinterface of ExecutorService, supports future and/or periodic execution of tasks.
The Executor Interface
The Executor
interface provides a single method, execute, designed to be a drop-in replacement for a common thread-creation idiom. If r is a Runnable object, and e is an Executor object you can replace
(new Thread(r)).start();
with
e.execute(r);
Drawbacks
The Executor
implementation, execute may do the same thing, but is more likely to use an existing worker thread to run r, or to place r in a queue to wait for a worker thread to become available.
The ExecutorService
ExecutorService
can execute Runnable
and Callable
tasks.
How to instantiate?
https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/Executors.html
Assigning tasks to ExecutorService
We can assign tasks to the ExecutorService
using several methods including execute()
, which is inherited from the Executor
interface, and also submit()
, invokeAny()
and invokeAll()
.
The execute()
method is void and doesn’t give any possibility to get the result of a task’s execution or to check the task’s status (is it running)
executorService.execute(runnableTask);
submit()
submits a Callable
or a Runnable
task to an ExecutorService
and returns a result of type Future:
See LaunchingCallableUsingExecutorService.java
Future<String> future = executorService.submit(callableTask);
invokeAny()
assigns a collection of tasks to an ExecutorService
, causing each to run, and returns the result of a successful execution of one task (if there was a successful execution):
String result = executorService.invokeAny(callableTasks);
invokeAll()
assigns a collection of tasks to an ExecutorService
, causing each to run, and returns the result of all task executions in the form of a list of objects of type Future:
List<Future<String>> futures = executorService.invokeAll(callableTasks);
Common pitfalls
Despite the relative simplicity of ExecutorService, there are a few common pitfalls.
- Keeping an unused ExecutorService alive: See the detailed explanation in Section 4 on how to shut down an ExecutorService.
- Wrong thread-pool capacity while using fixed length thread pool: It is very important to determine how many threads the application will need to run tasks efficiently. A too-large thread pool will cause unnecessary overhead just to create threads that will mostly be in the waiting mode. Too few can make an application seem unresponsive because of long waiting periods for tasks in the queue.
- Calling a Future‘s get() method after task cancellation: Attempting to get the result of an already canceled task triggers a CancellationException.
- Unexpectedly long blocking with Future‘s get() method: We should use timeouts to avoid unexpected waits.
References
- https://docs.oracle.com/javase/tutorial/essential/concurrency/exinter.html
- https://www.baeldung.com/java-executor-service-tutorial