Page content

Threads and ExecutorService

Thread types
Thread priority
Runnable/Callable
Thread creation
Future object
ExecutorService-Single Thread
ExecutorService-Thread Pool

Synchronizing Data Access

Threads and ExecutorService

Concurrency is a property of executing multiple threads and processes at the same time.

Keywords: Single-threaded Process, Multi-threaded Process, Task, Concurrency, Thread Scheduler, Context Switch, Thread Scheduler, Thread Priority, ExecutorService

Thread types

– system thread – created by the JVM and runs in the background of the application
– user-defined thread – created by the application developer to accomplish a specific task
– daemon thread – one that will not prevent the JVM from exiting when the program finishes

Thread priorities

Constant Variable Value
Thread.MIN_PRIORITY 1
Thread.NORM_PRIORITY 5
Thread.MAX_PRIORITY 10

There are no guarantees about the order in which a thread will be processed once it is started. Could be immediately or delayed.

Runnable and Callable

– functional interfaces
– used to define the work a thread will execute, separate from the main application thread

Runnable

– method run() takes no arguments and returns no data
– cannot throw any exceptions
@FunctionalInterface public interface Runnable { void run();
}

Callable

– method call() takes no arguments and returned a value
– throws a checked exception
@FunctionalInterface public interface Callable { V call() throws Exception;
}

Creating a Thread

There are two steps when creating a new thread:

  1. write the task (implement Runnable interface or override Thread class)
  2. start the thread (Thread.start())

There are two ways of creating a thread:

  1. provide a class implementing Runnable or lambda expression directly to Threads constructor (more common use)
  2. override Thread class and implement run() method (when creating our own priority-based thread)

Future object

Determines the state of the task

Method Description
boolean isDone() true if the task was completed, threw an exception or was cancelled
boolean isCancelled() true if the task was cancelled
boolean cancel() cancel the task
V get() get the results of the task (null or object returned by Callable), waiting until the result is available
V get(long timeout, TimeUnit unit) get the result, but wait only specified amount of time, if the result is still not available throw TimeoutException

ExecutorService

Framework includes numerous useful features, making it easier to create and manage threads.
ExecutorService is an interface, to obtain an instance, we use Executors factory.

Single-Thread Executor

ExecutorService service = null;
try {
service = Executors.newSingleThreadExecutor();
service.execute(() -> System.out.println("Provide lambda expression or Runnable object!"));
} finally {
if(service != null) service.shutdown();
}

Results are to be executed in the order in which they are added to the executor.

It’s crucial to shutdown the executor. A thread executor creates a non-daemon thread, if not shutdown, it will result in never ending application.

shutdown() shutdownNow()
isShutdown() true true
isTerminated() false true
Submitted tasks, that have already been started continue to execute attempt to stop*
Submitted tasks, that haven’t been started yet continue to execute discard
Submitting new tasks RejectedExecutionException RejectedExecutionException
Return type void List>Runnable>

*thread that can’t be terminated – any attempt to interrupt it may be ignored
**tasks that were submitted to the thread executor but that were never started.

ExecutorService interface does not implement AutoCloseable!

Submitting a task
Method Description
void execute(Runnable command) execute a Task (Runnable) in the future, fire-and-forget
Future<?> submit(Runnable task) execute a Task (Runnable) in the future, return Future object
Future submit(Callable task) execute a Task (Callable) in the future, return Future object (pending results of the task)
*List<Future> invokeAll( Collection<? extends Callable> tasks) throws InterruptedException/td> executing Tasks (Callable), return a Collection of Future objects
*T invokeAny(
Collection<? extends Callable> tasks) throws InterruptedException, ExecutionException
executing Tasks (Callable), return a result of one of finished tasks, cancelling any unfinished tasks

*invokeAll and invokeAny have overloaded versions accepting timeout value and TimeUnit parameter.

ScheduledExecutorService

When to use:
– when we need to execute a task in the future
– when we need to execute a task repeatedly

We use ScheduledExecutorService interface and a factory method Executors to obtain an instance.

Example:
ScheduledExecutorService service = null;
try {
service = Executors.newSingleThreadScheduledExecutor();
// continue...
} finally {
if(service != null) service.shutdown();
}

Method Description
schedule(Callable callable, long delay, TimeUnit unit) executing a task (Callable), after the provided delay
schedule(Runnable command, long delay, TimeUnit unit) executing a task (Runnable), after the provided delay
scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) executing a task (Runnable) after the provided delay, creating new task every period defined
scheduleAtFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) executing a task (Runnable) after the provided delay, subsequently with the provided delay between the termination of one execution and the beginning of the newt one

ScheduledFuture is very similar to Future class, with the difference that it includes a getDelay() method. This method returns the delay set when the process was created.
Observe that scheduleAtFixedRate() and scheduleAtFixedDelay() both accept only Runnable objects, this prevents the generation of endless series of Future objects.

Thread Pool

ExecutorService and ScheduledExecutorService provide also methods for creation of pools of threads (not only a single thread).
These are the methods:

Method Description
newCachedThreadPool() creates a thread pool, which reuses already constructed and available threads, creates new if no are available
newFixedThreadPool(int nThreads) creates a thread pool, with fixed number of threads
newScheduledThreadPool(int nThreads) creates a thread pool, with fixed number of threads and allows scheduling
Keywords explained

Context Switch = process of storing thread’s state and restoring later when continuing the execution
Thread Scheduler = determines which threads to execute at a given time (e.g. Round-Robin Schedule)