Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
196 views

Advanced Concurrency in Java

1. Daemon threads are low-priority threads that help user threads run but are not essential. The JVM will exit once all user threads finish even if daemon threads are still running. Daemon threads are useful for background tasks like garbage collection. 2. ExecutorService simplifies running tasks asynchronously using a thread pool. It can execute Runnable and Callable tasks submitted to it. ExecutorService must be shut down properly to avoid memory leaks, by first calling shutdown() and then waiting for termination or calling shutdownNow(). 3. The Future interface allows getting results from and checking status of tasks submitted to ExecutorService. Its get() method blocks until a Callable task completes and returns its result.
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
196 views

Advanced Concurrency in Java

1. Daemon threads are low-priority threads that help user threads run but are not essential. The JVM will exit once all user threads finish even if daemon threads are still running. Daemon threads are useful for background tasks like garbage collection. 2. ExecutorService simplifies running tasks asynchronously using a thread pool. It can execute Runnable and Callable tasks submitted to it. ExecutorService must be shut down properly to avoid memory leaks, by first calling shutdown() and then waiting for termination or calling shutdownNow(). 3. The Future interface allows getting results from and checking status of tasks submitted to ExecutorService. Its get() method blocks until a Callable task completes and returns its result.
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 68

1

Advanced Concurrency in Java

Daemon Threads in Java

1. Overview

In this short article, we'll have a look at daemon threads in Java and see what can they be
used for. We'll also explain the difference between daemon threads and user threads.

2. Difference Between Daemon and User Threads

Java offers two types of threads: user threads and daemon threads.
User threads are high-priority threads. The JVM will wait for any user thread to complete
its task before terminating it.
On the other hand, daemon threads are low-priority threads whose only role is to provide
services to user threads.
Since daemon threads are meant to serve user threads and are only needed while user threads
are running, they won't prevent the JVM from exiting once all user threads have finished their
execution.
That's why infinite loops, which typically exist in daemon threads, will not cause problems,
because any code, including the finally blocks, won't be executed once all user threads have
finished their execution. For this reason, daemon threads are not recommended for I/O
tasks.
However, there're exceptions to this rule. Poorly designed code in daemon threads can
prevent the JVM from exiting. For example, calling Thread.join() on a running daemon
thread can block the shutdown of the application.

3. Uses of Daemon Threads

Daemon threads are useful for background supporting tasks such as garbage collection,
releasing memory of unused objects and removing unwanted entries from the cache. Most of
the JVM threads are daemon threads.

4. Creating a Daemon Thread

To set a thread to be a daemon thread, all we need to do is to call Thread.setDaemon(). In


this example, we'll use the NewThread class which extends the Thread class:
NewThread daemonThread = new NewThread();
daemonThread.setDaemon(true);
daemonThread.start();

Any thread inherits the daemon status of the thread that created it. Since the main thread
is a user thread, any thread that is created inside the main method is by default a user thread.
2
Advanced Concurrency in Java

The method setDaemon() can only be called after the Thread object has been created and the
thread has not been started. An attempt to call setDaemon() while a thread is running will
throw an IllegalThreadStateException:
@Test(expected = IllegalThreadStateException.class)
public void whenSetDaemonWhileRunning_thenIllegalThreadStateException() {
NewThread daemonThread = new NewThread();
daemonThread.start();
daemonThread.setDaemon(true);
}

5. Checking if a Thread Is a Daemon Thread

Finally, to check if a thread is a daemon thread, we can simply call the method isDaemon():
@Test
public void whenCallIsDaemon_thenCorrect() {
NewThread daemonThread = new NewThread();
NewThread userThread = new NewThread();
daemonThread.setDaemon(true);
daemonThread.start();
userThread.start();

assertTrue(daemonThread.isDaemon());
assertFalse(userThread.isDaemon());
}

6. Conclusion

In this quick tutorial, we've seen what daemon threads are and what they can be used for in a
few practical scenarios.

A Guide to the Java ExecutorService

1. Overview

ExecutorService is a framework provided by the JDK which simplifies the execution of tasks
in asynchronous mode. Generally speaking, ExecutorService automatically provides a pool of
threads and API for assigning tasks to it.

2. Instantiating ExecutorService

2.1. Factory Methods of the Executors Class


3
Advanced Concurrency in Java

The easiest way to create ExecutorService is to use one of the factory methods of


the Executors class.
For example, the following line of code will create a thread-pool with 10 threads:

ExecutorService executor = Executors.newFixedThreadPool(10);

The are several other factory methods to create predefined ExecutorService that meet specific
use cases. To find the best method for your needs, consult Oracle's official documentation.

2.2. Directly Create an ExecutorService

Because ExecutorService is an interface, an instance of any its implementations can be used.


There are several implementations to choose from in the java.util.concurrent package or you
can create your own.
For example, the ThreadPoolExecutor class has a few constructors which can be used to
configure an executor service and its internal pool.
ExecutorService executorService =
new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
You may notice that the code above is very similar to the source code of the factory
method newSingleThreadExecutor(). For most cases, a detailed manual configuration isn't
necessary.

3. Assigning Tasks to the ExecutorService

ExecutorService can execute Runnable and Callable tasks. To keep things simple in this


article, two primitive tasks will be used. Notice that lambda expressions are used here instead
of anonymous inner classes:
Runnable runnableTask = () -> {
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
};

Callable<String> callableTask = () -> {


TimeUnit.MILLISECONDS.sleep(300);
return "Task's execution";
};

List<Callable<String>> callableTasks = new ArrayList<>();


callableTasks.add(callableTask);
4
Advanced Concurrency in Java

callableTasks.add(callableTask);
callableTasks.add(callableTask);
Tasks can be assigned to the ExecutorService using several methods, including execute(),
which is inherited from the Executor interface, and also submit(), invokeAny(), invokeAll().
The execute() method is void,  and it doesn't give any possibility to get the result of task's
execution or to check the task's status (is it running or executed).
executorService.execute(runnableTask);

submit() submits a Callable or a Runnable task to an ExecutorService and returns a result of


type Future.
Future<String> future =
executorService.submit(callableTask);

invokeAny() assigns a collection of tasks to an ExecutorService, causing each to be executed,


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 be executed,


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

Now, before going any further, two more things must be discussed: shutting down
an ExecutorService and dealing with Future return types.

4. Shutting Down an ExecutorService

In general, the ExecutorService will not be automatically destroyed when there is no task to


process. It will stay alive and wait for new work to do.
In some cases this is very helpful; for example, if an app needs to process tasks that appear on
an irregular basis or the quantity of these tasks is not known at compile time.
On the other hand, an app could reach its end, but it will not be stopped because a
waiting ExecutorService will cause the JVM to keep running.
To properly shut down an ExecutorService, we have
the shutdown() and shutdownNow() APIs.
The shutdown() method doesn't cause immediate destruction of the ExecutorService. It will
make the ExecutorService stop accepting new tasks and shut down after all running threads
finish their current work.
executorService.shutdown();
The shutdownNow() method tries to destroy the ExecutorService immediately, but it doesn't
guarantee that all the running threads will be stopped at the same time. This method returns a
list of tasks that are waiting to be processed. It is up to the developer to decide what to do
with these tasks.
5
Advanced Concurrency in Java

List<Runnable> notExecutedTasks = executorService.shutDownNow();


One good way to shut down the ExecutorService (which is also recommended by Oracle) is
to use both of these methods combined with the awaitTermination() method. With this
approach, the ExecutorService will first stop taking new tasks, then wait up to a specified
period of time for all tasks to be completed. If that time expires, the execution is stopped
immediately:
executorService.shutdown();
try {
if (!executorService.awaitTermination(800, TimeUnit.MILLISECONDS)) {
executorService.shutdownNow();
}
} catch (InterruptedException e) {
executorService.shutdownNow();
}

5. The Future Interface

The submit() and invokeAll() methods return an object or a collection of objects of


type Future, which allows us to get the result of a task's execution or to check the task's status
(is it running or executed).
The Future interface provides a special blocking method get() which returns an actual result
of the Callable  task's execution or null in the case of Runnable task. Calling the get() method
while the task is still running will cause execution to block until the task is properly executed
and the result is available.
Future<String> future = executorService.submit(callableTask);
String result = null;
try {
result = future.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}

With very long blocking caused by the get() method, an application's performance can


degrade. If the resulting data is not crucial, it is possible to avoid such a problem by using
timeouts:
String result = future.get(200, TimeUnit.MILLISECONDS);

If the execution period is longer than specified (in this case 200 milliseconds),
a TimeoutException will be thrown.
The isDone() method can be used to check if the assigned task is already processed or not.
The Future interface also provides for the cancellation of task execution with
the cancel() method, and to check the cancellation with isCancelled() method:
boolean canceled = future.cancel(true);
boolean isCancelled = future.isCancelled();
6
Advanced Concurrency in Java

6. The ScheduledExecutorService Interface

The ScheduledExecutorService runs tasks after some predefined delay and/or periodically.


Once again, the best way to instantiate a ScheduledExecutorService is to use the factory
methods of the Executors class.
For this section, a ScheduledExecutorService with one thread will be used:
ScheduledExecutorService executorService = Executors
.newSingleThreadScheduledExecutor();

To schedule a single task's execution after a fixed delay, us the scheduled() method of


the ScheduledExecutorService. There are two scheduled() methods that allow you to
execute Runnable or Callable  tasks:
Future<String> resultFuture =
executorService.schedule(callableTask, 1, TimeUnit.SECONDS);

The scheduleAtFixedRate() method lets execute a task periodically after a fixed delay. The


code above delays for one second before executing callableTask.
The following block of code will execute a task after an initial delay of 100 milliseconds, and
after that, it will execute the same task every 450 milliseconds. If the processor needs more
time to execute an assigned task than the period parameter of
the scheduleAtFixedRate() method, the ScheduledExecutorService will wait until the current
task is completed before starting the next:
Future<String> resultFuture = service
.scheduleAtFixedRate(runnableTask, 100, 450, TimeUnit.MILLISECONDS);

If it is necessary to have a fixed length delay between iterations of the


task, scheduleWithFixedDelay() should be used. For example, the following code will
guarantee a 150-millisecond pause between the end of the current execution and the start of
another one.
service.scheduleWithFixedDelay(task, 100, 150, TimeUnit.MILLISECONDS);

According to the scheduleAtFixedRate() and scheduleWithFixedDelay() method contracts,


period execution of the task will end at the termination of the ExecutorService or if an
exception is thrown during task execution.

7. ExecutorService vs. Fork/Join

After the release of Java 7, many developers decided that the ExecutorService framework


should be replaced by the fork/join framework. This is not always the right decision,
however. Despite the simplicity of usage and the frequent performance gains associated with
fork/join, there is also a reduction in the amount of developer control over concurrent
execution.
ExecutorService gives the developer the ability to control the number of generated threads
and the granularity of tasks which should be executed by separate threads. The best use case
7
Advanced Concurrency in Java

for ExecutorService is the processing of independent tasks, such as transactions or requests


according to the scheme “one thread for one task.”
In contrast, according to Oracle's documentation, fork/join was designed to speed up work
which can be broken into smaller pieces recursively.

8. Conclusion

Even despite the relative simplicity of ExecutorService, there are a few common pitfalls. Let's
summarize them:
Keeping an unused ExecutorService alive: There is a detailed explanation in section 4 of
this article about 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 execute tasks efficiently. A thread-
pool that is too large will cause unnecessary overhead just to create threads which mostly will
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: An attempt to get the result of an
already canceled task will trigger a CancellationException.
Unexpectedly-long blocking with Future‘s get() method: Timeouts should be used to avoid
unexpected waits.

Guide to the Fork/Join Framework in Java

1. Overview

The fork/join framework was presented in Java 7. It provides tools to help speed up parallel
processing by attempting to use all available processor cores – which is
accomplished through a divide and conquer approach.
In practice, this means that the framework first “forks”, recursively breaking the task into
smaller independent subtasks until they are simple enough to be executed asynchronously.
After that, the “join” part begins, in which results of all subtasks are recursively joined into
a single result, or in the case of a task which returns void, the program simply waits until
every subtask is executed.
To provide effective parallel execution, the fork/join framework uses a pool of threads called
the ForkJoinPool, which manages worker threads of type ForkJoinWorkerThread.
8
Advanced Concurrency in Java

2. ForkJoinPool

The ForkJoinPool is the heart of the framework. It is an implementation of


the ExecutorService that manages worker threads and provides us with tools to get
information about the thread pool state and performance.
Worker threads can execute only one task at a time, but the ForkJoinPool doesn’t create a
separate thread for every single subtask. Instead, each thread in the pool has its own double-
ended queue (or deque, pronounced deck) which stores tasks.
This architecture is vital for balancing the thread’s workload with the help of the work-
stealing algorithm.

2.1. Work Stealing Algorithm

Simply put – free threads try to “steal” work from deques of busy threads.
By default, a worker thread gets tasks from the head of its own deque. When it is empty, the
thread takes a task from the tail of the deque of another busy thread or from the global entry
queue, since this is where the biggest pieces of work are likely to be located.
This approach minimizes the possibility that threads will compete for tasks. It also reduces
the number of times the thread will have to go looking for work, as it works on the biggest
available chunks of work first.

2.2. ForkJoinPool Instantiation

In Java 8, the most convenient way to get access to the instance of the ForkJoinPool is to use
its static method commonPool().  As its name suggests, this will provide a reference to the
common pool, which is a default thread pool for every ForkJoinTask.
According to Oracle’s documentation, using the predefined common pool reduces resource
consumption, since this discourages the creation of a separate thread pool per task.
ForkJoinPool commonPool = ForkJoinPool.commonPool();
The same behavior can be achieved in Java 7 by creating a ForkJoinPool and assigning it to
a public static field of a utility class:
public static ForkJoinPool forkJoinPool = new ForkJoinPool(2);
Now it can be easily accessed:
ForkJoinPool forkJoinPool = PoolUtil.forkJoinPool;
With ForkJoinPool’s constructors, it is possible to create a custom thread pool with a
specific level of parallelism, thread factory, and exception handler. In the example above, the
pool has a parallelism level of 2. This means that pool will use 2 processor cores.

3. ForkJoinTask<V>

ForkJoinTask is the base type for tasks executed inside ForkJoinPool. In practice, one of its
two subclasses should be extended: the RecursiveAction for void tasks and
9
Advanced Concurrency in Java

the RecursiveTask<V> for tasks that return a value. They both have an abstract


method compute() in which the task’s logic is defined.

3.1. RecursiveAction – an Example

In the example below, the unit of work to be processed is represented by


a String called workload. For demonstration purposes, the task is a nonsensical one: it simply
uppercases its input and logs it.
To demonstrate the forking behavior of the framework, the example splits the task
if workload.length() is larger than a specified threshold using the createSubtask() method.
The String is recursively divided into substrings, creating CustomRecursiveTask instances
which are based on these substrings.
As a result, the method returns a List<CustomRecursiveAction>.
The list is submitted to the ForkJoinPool  using the invokeAll() method:
public class CustomRecursiveAction extends RecursiveAction {

private String workload = "";


private static final int THRESHOLD = 4;

private static Logger logger =


Logger.getAnonymousLogger();

public CustomRecursiveAction(String workload) {


this.workload = workload;
}

@Override
protected void compute() {
if (workload.length() > THRESHOLD) {
ForkJoinTask.invokeAll(createSubtasks());
} else {
processing(workload);
}
}

private List<CustomRecursiveAction> createSubtasks() {


List<CustomRecursiveAction> subtasks = new ArrayList<>();

String partOne = workload.substring(0, workload.length() / 2);


String partTwo = workload.substring(workload.length() / 2, workload.length());

subtasks.add(new CustomRecursiveAction(partOne));
subtasks.add(new CustomRecursiveAction(partTwo));
10
Advanced Concurrency in Java

return subtasks;
}

private void processing(String work) {


String result = work.toUpperCase();
logger.info("This result - (" + result + ") - was processed by "
+ Thread.currentThread().getName());
}
}
This pattern can be used to develop your own RecursiveAction classes. To do this, create an
object which represents the total amount of work, chose a suitable threshold, define a method
to divide the work, and define a method to do the work.

3.2. RecursiveTask<V>

For tasks that return a value, the logic here is similar, except that the result for each subtask is
united in a single result:
public class CustomRecursiveTask extends RecursiveTask<Integer> {
private int[] arr;

private static final int THRESHOLD = 20;

public CustomRecursiveTask(int[] arr) {


this.arr = arr;
}

@Override
protected Integer compute() {
if (arr.length > THRESHOLD) {
return ForkJoinTask.invokeAll(createSubtasks())
.stream()
.mapToInt(ForkJoinTask::join)
.sum();
} else {
return processing(arr);
}
}

private Collection<CustomRecursiveTask> createSubtasks() {


List<CustomRecursiveTask> dividedTasks = new ArrayList<>();
dividedTasks.add(new CustomRecursiveTask(
Arrays.copyOfRange(arr, 0, arr.length / 2)));
dividedTasks.add(new CustomRecursiveTask(
Arrays.copyOfRange(arr, arr.length / 2, arr.length)));
return dividedTasks;
11
Advanced Concurrency in Java

private Integer processing(int[] arr) {


return Arrays.stream(arr)
.filter(a -> a > 10 && a < 27)
.map(a -> a * 10)
.sum();
}
}
In this example, the work is represented by an array stored in the arr field of
the CustomRecursiveTask class. The createSubtasks() method recursively divides the task
into smaller pieces of work until each piece is smaller than the threshold. Then,
the invokeAll() method submits the subtasks to the common pool and returns a list of Future.
To trigger execution, the join() method is called for each subtask.
In this example, this is accomplished using Java 8's Stream API; the sum() method is used as
a representation of combining sub results into the final result.

4. Submitting Tasks to the ForkJoinPool

To submit tasks to the thread pool, few approaches can be used.


The submit()  or execute() method (their use cases are the same):
forkJoinPool.execute(customRecursiveTask);
int result = customRecursiveTask.join();
The invoke() method forks the task and waits for the result, and doesn’t need any manual
joining:
int result = forkJoinPool.invoke(customRecursiveTask);
The invokeAll() method is the most convenient way to submit a sequence
of ForkJoinTasks to the ForkJoinPool. It takes tasks as parameters (two tasks, var args, or a
collection), forks then returns a collection of Future objects in the order in which they were
produced.
Alternatively, you can use separate fork() and join() methods. The fork() method submits a
task to a pool, but it doesn't trigger its execution. The join() method must be used for this
purpose. In the case of RecursiveAction, the join() returns nothing but null;
for RecursiveTask<V>,  it returns the result of the task's execution:
customRecursiveTaskFirst.fork();
result = customRecursiveTaskLast.join();
In our RecursiveTask<V> example we used the invokeAll() method to submit a sequence of
subtasks to the pool. The same job can be done with fork() and join(), though this has
consequences for the ordering of the results.
To avoid confusion, it is generally a good idea to use invokeAll() method to submit more than
one task to the ForkJoinPool.
12
Advanced Concurrency in Java

5. Conclusions

Using the fork/join framework can speed up processing of large tasks, but to achieve this
outcome, some guidelines should be followed:

 Use as few thread pools as possible – in most cases, the best decision is to use one
thread pool per application or system
 Use the default common thread pool, if no specific tuning is needed
 Use a reasonable threshold for splitting ForkJoinTask into subtasks
 Avoid any blocking in your ForkJoinTasks
13
Advanced Concurrency in Java

Custom Thread Pools In Java 8 Parallel Streams

1. Overview

Java 8 introduced the concept of Streams as an efficient way of carrying out bulk operations
on data. And parallel Streams can be obtained in environments that support concurrency.
These streams can come with improved performance – at the cost of multi-threading
overhead.
In this quick tutorial, we'll look at one of the biggest limitations of Stream API and see how
to make a parallel stream work with a custom ThreadPool instance, alternatively – there's a
library that handles this.

2. Parallel Stream

Let's start with a simple example – calling the parallelStream method on any of


the Collection types – which will return a possibly parallel Stream:
@Test
public void givenList_whenCallingParallelStream_shouldBeParallelStream(){
List<Long> aList = new ArrayList<>();
Stream<Long> parallelStream = aList.parallelStream();

assertTrue(parallelStream.isParallel());
}
The default processing that occurs in such a Stream uses
the ForkJoinPool.commonPool(), a Thread Pool shared by the entire application.

3. Custom Thread Pool

We can actually pass a custom ThreadPool  when processing the stream.


The following example lets have a parallel Stream use a custom Thread Pool to calculate the
sum of long values from 1 to 1,000,000, inclusive:
@Test
public void
giveRangeOfLongs_whenSummedInParallel_shouldBeEqualToExpectedTotal()
throws InterruptedException, ExecutionException {

long firstNum = 1;
long lastNum = 1_000_000;

List<Long> aList = LongStream.rangeClosed(firstNum, lastNum).boxed()


.collect(Collectors.toList());
14
Advanced Concurrency in Java

ForkJoinPool customThreadPool = new ForkJoinPool(4);


long actualTotal = customThreadPool.submit(
() -> aList.parallelStream().reduce(0L, Long::sum)).get();

assertEquals((lastNum + firstNum) * lastNum / 2, actualTotal);


}
We used the ForkJoinPool constructor with a parallelism level of 4. Some experimentation is
required to determine the optimal value for different environments, but a good rule of thumb
is simply choosing the number based on how many cores your CPU has.
Next, we processed the content of the parallel Stream, summing them up in the reduce call.
This simple example may not demonstrate the full usefulness of using a custom Thread Pool,
but the benefits become obvious in situations where we do not want to tie-up the
common Thread Pool with long-running tasks (e.g. processing data from a network source),
or the common Thread Pool is being used by other components within the application.

4. Conclusion

We have briefly looked at how to run a parallel Stream using a custom Thread Pool. In the


right environment and with the proper use of the parallelism level, performance gains can be
had in certain situations.
15
Advanced Concurrency in Java

Guide to CountDownLatch in Java

1. Introduction

In this article, we'll give a guide to the CountDownLatch class and demonstrate how it can be
used in a few practical examples.
Essentially, by using a CountDownLatch we can cause a thread to block until other threads
have completed a given task.

2. Usage in Concurrent Programming

Simply put, a CountDownLatch has a counter field, which you can decrement as we require.


We can then use it to block a calling thread until it's been counted down to zero.
If we were doing some parallel processing, we could instantiate the CountDownLatch with
the same value for the counter as a number of threads we want to work across. Then, we
could just call countdown() after each thread finishes, guaranteeing that a dependent thread
calling await() will block until the worker threads are finished.

3. Waiting for a Pool of Threads to Complete

Let's try out this pattern by creating a Worker and using a CountDownLatch field to signal
when it has completed:
public class Worker implements Runnable {
private List<String> outputScraper;
private CountDownLatch countDownLatch;

public Worker(List<String> outputScraper, CountDownLatch countDownLatch) {


this.outputScraper = outputScraper;
this.countDownLatch = countDownLatch;
}

@Override
public void run() {
doSomeWork();
outputScraper.add("Counted down");
countDownLatch.countDown();
}
}
Then, let's create a test in order to prove that we can get a CountDownLatch to wait for
the Worker instances to complete:
16
Advanced Concurrency in Java

@Test
public void whenParallelProcessing_thenMainThreadWillBlockUntilCompletion()
throws InterruptedException {

List<String> outputScraper = Collections.synchronizedList(new ArrayList<>());


CountDownLatch countDownLatch = new CountDownLatch(5);
List<Thread> workers = Stream
.generate(() -> new Thread(new Worker(outputScraper, countDownLatch)))
.limit(5)
.collect(toList());

workers.forEach(Thread::start);
countDownLatch.await();
outputScraper.add("Latch released");

assertThat(outputScraper)
.containsExactly(
"Counted down",
"Counted down",
"Counted down",
"Counted down",
"Counted down",
"Latch released"
);
}
Naturally “Latch released” will always be the last output – as it's dependant on
the CountDownLatch  releasing.
Note that if we didn't call await(), we wouldn't be able to guarantee the ordering of the
execution of the threads, so the test would randomly fail.

4. A Pool of Threads Waiting to Begin

If we took the previous example, but this time started thousands of threads instead of five, it's
likely that many of the earlier ones will have finished processing before we have even
called start() on the later ones. This could make it difficult to try and reproduce a
concurrency problem, as we wouldn't be able to get all our threads to run in parallel.
To get around this, let's get the CountdownLatch to work differently than in the previous
example. Instead of blocking a parent thread until some child threads have finished, we can
block each child thread until all the others have started.
Let's modify our run() method so it blocks before processing:
public class WaitingWorker implements Runnable {

private List<String> outputScraper;


private CountDownLatch readyThreadCounter;
17
Advanced Concurrency in Java

private CountDownLatch callingThreadBlocker;


private CountDownLatch completedThreadCounter;

public WaitingWorker(
List<String> outputScraper,
CountDownLatch readyThreadCounter,
CountDownLatch callingThreadBlocker,
CountDownLatch completedThreadCounter) {

this.outputScraper = outputScraper;
this.readyThreadCounter = readyThreadCounter;
this.callingThreadBlocker = callingThreadBlocker;
this.completedThreadCounter = completedThreadCounter;
}

@Override
public void run() {
readyThreadCounter.countDown();
try {
callingThreadBlocker.await();
doSomeWork();
outputScraper.add("Counted down");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
completedThreadCounter.countDown();
}
}
}
Now, let's modify our test so it blocks until all the Workers have started, unblocks
the Workers, and then blocks until the Workers have finished:
@Test
public void whenDoingLotsOfThreadsInParallel_thenStartThemAtTheSameTime()
throws InterruptedException {

List<String> outputScraper = Collections.synchronizedList(new ArrayList<>());


CountDownLatch readyThreadCounter = new CountDownLatch(5);
CountDownLatch callingThreadBlocker = new CountDownLatch(1);
CountDownLatch completedThreadCounter = new CountDownLatch(5);
List<Thread> workers = Stream
.generate(() -> new Thread(new WaitingWorker(
outputScraper, readyThreadCounter, callingThreadBlocker,
completedThreadCounter)))
.limit(5)
.collect(toList());
18
Advanced Concurrency in Java

workers.forEach(Thread::start);
readyThreadCounter.await();
outputScraper.add("Workers ready");
callingThreadBlocker.countDown();
completedThreadCounter.await();
outputScraper.add("Workers complete");

assertThat(outputScraper)
.containsExactly(
"Workers ready",
"Counted down",
"Counted down",
"Counted down",
"Counted down",
"Counted down",
"Workers complete"
);
}
This pattern is really useful for trying to reproduce concurrency bugs, as can be used to force
thousands of threads to try and perform some logic in parallel.

5. Terminating a CountdownLatch Early

Sometimes, we may run into a situation where the Workers terminate in error before counting
down the CountDownLatch. This could result in it never reaching zero and await() never
terminating:
@Override
public void run() {
if (true) {
throw new RuntimeException("Oh dear, I'm a BrokenWorker");
}
countDownLatch.countDown();
outputScraper.add("Counted down");
}
Let's modify our earlier test to use a BrokenWorker, in order to show how await() will block
forever:
@Test
public void whenFailingToParallelProcess_thenMainThreadShouldGetNotGetStuck()
throws InterruptedException {

List<String> outputScraper = Collections.synchronizedList(new ArrayList<>());


CountDownLatch countDownLatch = new CountDownLatch(5);
List<Thread> workers = Stream
.generate(() -> new Thread(new BrokenWorker(outputScraper,
countDownLatch)))
19
Advanced Concurrency in Java

.limit(5)
.collect(toList());

workers.forEach(Thread::start);
countDownLatch.await();
}
Clearly, this is not the behavior we want – it would be much better for the application to
continue than infinitely block.
To get around this, let's add a timeout argument to our call to await().
boolean completed = countDownLatch.await(3L, TimeUnit.SECONDS);
assertThat(completed).isFalse();
As we can see, the test will eventually time out and await() will return false.

6. Conclusion

In this quick guide, we've demonstrated how we can use a CountDownLatch in order to block
a thread until other threads have finished some processing.
We've also shown how it can be used to help debug concurrency issues by making sure
threads run in parallel.
20
Advanced Concurrency in Java

Guide to java.util.concurrent.Locks

1. Overview

Simply put, a lock is a more flexible and sophisticated thread synchronization mechanism
than the standard synchronized block.
The Lock interface has been around since Java 1.5. It's defined inside
the java.util.concurrent.lock package and it provides extensive operations for locking.
In this article, we'll explore different implementations of the Lock interface and their
applications.

2. Differences Between Lock and Synchronized Block

There are few differences between the use of synchronized block and using Lock API's:

 A synchronized block is fully contained within a method – we can


have Lock API's lock() and unlock() operation in separate methods
 A synchronized block doesn't support the fairness, any thread can acquire the lock
once released, no preference can be specified. We can achieve fairness within
the Lock APIs by specifying the fairness property. It makes sure that longest
waiting thread is given access to the lock
 A thread gets blocked if it can't get an access to the
synchronized block. The Lock API provides tryLock()  method. The thread
acquires lock only if it's available and not held by any other thread. This reduces
blocking time of thread waiting for the lock
 A thread which is in “waiting” state to acquire the access to synchronized block, can't
be interrupted. The Lock API provides a method lockInterruptibly() which can be
used to interrupt the thread when it's waiting for the lock

3. Lock API

Let's take a look at the methods in the Lock interface:

 void lock() – acquire the lock if it's available; if the lock isn't available a thread gets
blocked until the lock is released
 void lockInterruptibly() – this is similar to the lock(),  but it allows the blocked thread
to be interrupted and resume the execution through a
thrown java.lang.InterruptedException
 boolean tryLock() – this is a non-blocking version of lock()  method; it attempts to
acquire the lock immediately, return true if locking succeeds
 boolean tryLock(long timeout, TimeUnit timeUnit) – this is similar
to tryLock(),  except it waits up the given timeout before giving up trying to acquire
the Lock
 void unlock() – unlocks the Lock instance
21
Advanced Concurrency in Java

A locked instance should always be unlocked to avoid deadlock condition. A recommended


code block to use the lock should contain a try/catch and finally block:
Lock lock = ...;
lock.lock();
try {
// access to the shared resource
} finally {
lock.unlock();
}
In addition to the Lock interface, we have a ReadWriteLock interface which maintains a pair
of locks, one for read-only operations, and one for the write operation. The read lock may be
simultaneously held by multiple threads as long as there is no write.
ReadWriteLock declares methods to acquire read or write locks:

 Lock readLock() – returns the lock that's used for reading


 Lock writeLock() – returns the lock that's used for writing

4. Lock Implementations

4.1. ReentrantLock

ReentrantLock class implements the Lock interface. It offers the same concurrency and


memory semantics, as the implicit monitor lock accessed using synchronized methods and
statements, with extended capabilities.
Let's see, how we can use ReenrtantLock for synchronization:
public class SharedObject {
//...
ReentrantLock lock = new ReentrantLock();
int counter = 0;

public void perform() {


lock.lock();
try {
// Critical section here
count++;
} finally {
lock.unlock();
}
}
//...
}
We need to make sure that we are wrapping the lock() and the unlock() calls in the try-
finally block to avoid the deadlock situations.
22
Advanced Concurrency in Java

Let's see how the tryLock()  works:


public void performTryLock(){
//...
boolean isLockAcquired = lock.tryLock(1, TimeUnit.SECONDS);

if(isLockAcquired) {
try {
//Critical section here
} finally {
lock.unlock();
}
}
//...
}
In this case, the thread calling tryLock(), will wait for one second and will give up waiting if
the lock isn't available.

4.2. ReentrantReadWriteLock

ReentrantReadWriteLock class implements the ReadWriteLock interface.


Let's see rules for acquiring the ReadLock or WriteLock by a thread:

 Read Lock – if no thread acquired the write lock or requested for it then multiple
threads can acquire the read lock
 Write Lock – if no threads are reading or writing then only one thread can acquire the
write lock

Let's see how to make use of the ReadWriteLock:


public class SynchronizedHashMapWithReadWriteLock {

Map<String,String> syncHashMap = new HashMap<>();


ReadWriteLock lock = new ReentrantReadWriteLock();
// ...
Lock writeLock = lock.writeLock();

public void put(String key, String value) {


try {
writeLock.lock();
syncHashMap.put(key, value);
} finally {
writeLock.unlock();
}
}
...
public String remove(String key){
23
Advanced Concurrency in Java

try {
writeLock.lock();
return syncHashMap.remove(key);
} finally {
writeLock.unlock();
}
}
//...
}
For both the write methods, we need to surround the critical section with the write lock, only
one thread can get access to it:
Lock readLock = lock.readLock();
//...
public String get(String key){
try {
readLock.lock();
return syncHashMap.get(key);
} finally {
readLock.unlock();
}
}

public boolean containsKey(String key) {


try {
readLock.lock();
return syncHashMap.containsKey(key);
} finally {
readLock.unlock();
}
}
For both read methods, we need to surround the critical section with the read lock. Multiple
threads can get access to this section if no write operation is in progress.

4.3. StampedLock

StampedLock is introduced in Java 8. It also supports both read and write locks. However,
lock acquisition methods return a stamp that is used to release a lock or to check if the lock is
still valid:
public class StampedLockDemo {
Map<String,String> map = new HashMap<>();
private StampedLock lock = new StampedLock();

public void put(String key, String value){


long stamp = lock.writeLock();
try {
24
Advanced Concurrency in Java

map.put(key, value);
} finally {
lock.unlockWrite(stamp);
}
}

public String get(String key) throws InterruptedException {


long stamp = lock.readLock();
try {
return map.get(key);
} finally {
lock.unlockRead(stamp);
}
}
}
Another feature provided by StampedLock is optimistic locking. Most of the time read
operations don't need to wait for write operation completion and as a result of this, the full-
fledged read lock isn't required.
Instead, we can upgrade to read lock:
public String readWithOptimisticLock(String key) {
long stamp = lock.tryOptimisticRead();
String value = map.get(key);

if(!lock.validate(stamp)) {
stamp = lock.readLock();
try {
return map.get(key);
} finally {
lock.unlock(stamp);
}
}
return value;
}

5. Working With Conditions

The Condition class provides the ability for a thread to wait for some condition to occur while
executing the critical section.
This can occur when a thread acquires the access to the critical section but doesn't have the
necessary condition to perform its operation. For example, a reader thread can get access to
the lock of a shared queue, which still doesn't have any data to consume.
Traditionally Java provides wait(), notify() and notifyAll() methods for thread
intercommunication. Conditions have similar mechanisms, but in addition, we can specify
multiple conditions:
25
Advanced Concurrency in Java

public class ReentrantLockWithCondition {

Stack<String> stack = new Stack<>();


int CAPACITY = 5;

ReentrantLock lock = new ReentrantLock();


Condition stackEmptyCondition = lock.newCondition();
Condition stackFullCondition = lock.newCondition();

public void pushToStack(String item){


try {
lock.lock();
while(stack.size() == CAPACITY) {
stackFullCondition.await();
}
stack.push(item);
stackEmptyCondition.signalAll();
} finally {
lock.unlock();
}
}

public String popFromStack() {


try {
lock.lock();
while(stack.size() == 0) {
stackEmptyCondition.await();
}
return stack.pop();
} finally {
stackFullCondition.signalAll();
lock.unlock();
}
}
}

6. Conclusion

In this article, we have seen different implementations of the Lock interface and the newly
introduced StampedLock class. We also explored how we can make use of
the Condition class to work with multiple conditions.
26
Advanced Concurrency in Java

ExecutorService – Waiting for Threads to Finish

1. Overview

The ExecutorService framework makes it easy to process tasks in multiple threads. We're


going to exemplify some scenarios in which we wait for threads to finish their execution.
Also, we'll show how to gracefully shutdown an ExecutorService and wait for already
running threads to finish their execution.

2. After Executor's Shutdown

When using an Executor, we can shut it down by calling


the shutdown() or shutdownNow() methods. Although, it won't wait until all threads stop
executing.
Waiting for existing threads to complete their execution can be achieved by using
the awaitTermination() method.
This blocks the thread until all tasks complete their execution or the specified timeout is
reached:
public void awaitTerminationAfterShutdown(ExecutorService threadPool) {
threadPool.shutdown();
try {
if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) {
threadPool.shutdownNow();
}
} catch (InterruptedException ex) {
threadPool.shutdownNow();
Thread.currentThread().interrupt();
}
}

3. Using CountDownLatch

Next, let's look at another approach to solving this problem – using a CountDownLatch to
signal the completion of a task.
We can initialize it with a value that represents the number of times it can be decremented
before all threads, that have called the await() method, are notified.
For example, if we need the current thread to wait for another N threads to finish their
execution, we can initialize the latch using N:
ExecutorService WORKER_THREAD_POOL
= Executors.newFixedThreadPool(10);
27
Advanced Concurrency in Java

CountDownLatch latch = new CountDownLatch(2);


for (int i = 0; i < 2; i++) {
WORKER_THREAD_POOL.submit(() -> {
try {
// ...
latch.countDown();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}

// wait for the latch to be decremented by the two remaining threads


latch.await();

4. Using invokeAll()

The first approach that we can use to run threads is the invokeAll() method. The method
returns a list of Future objects after all tasks finish or the timeout expires.
Also, we must note that the order of the returned Future objects is the same as the list of the
provided Callable objects:
ExecutorService WORKER_THREAD_POOL =
Executors.newFixedThreadPool(10);

List<Callable<String>> callables = Arrays.asList(


new DelayedCallable("fast thread", 100),
new DelayedCallable("slow thread", 3000));

long startProcessingTime = System.currentTimeMillis();


List<Future<String>> futures = WORKER_THREAD_POOL.invokeAll(callables);

awaitTerminationAfterShutdown(WORKER_THREAD_POOL);

long totalProcessingTime = System.currentTimeMillis() - startProcessingTime;

assertTrue(totalProcessingTime >= 3000);

String firstThreadResponse = futures.get(0).get();

assertTrue("fast thread".equals(firstThreadResponse));

String secondThreadResponse = futures.get(1).get();


assertTrue("slow thread".equals(secondThreadResponse));
28
Advanced Concurrency in Java

5. Using ExecutorCompletionService

Another approach to running multiple threads is by using ExecutorCompletionService. It uses


a supplied ExecutorService to execute tasks.
One difference over invokeAll() is the order in which the Futures, representing the executed
tasks are returned. ExecutorCompletionService uses a queue to store the results in the
order they are finished, while invokeAll() returns a list having the same sequential order as
produced by the iterator for the given task list:
CompletionService<String> service
= new ExecutorCompletionService<>(WORKER_THREAD_POOL);

List<Callable<String>> callables = Arrays.asList(


new DelayedCallable("fast thread", 100),
new DelayedCallable("slow thread", 3000));

for (Callable<String> callable : callables) {


service.submit(callable);
}
The results can be accessed using the take() method:
long startProcessingTime = System.currentTimeMillis();

Future<String> future = service.take();


String firstThreadResponse = future.get();
long totalProcessingTime
= System.currentTimeMillis() - startProcessingTime;

assertTrue("First response should be from the fast thread",


"fast thread".equals(firstThreadResponse));
assertTrue(totalProcessingTime >= 100
&& totalProcessingTime < 1000);
LOG.debug("Thread finished after: " + totalProcessingTime
+ " milliseconds");

future = service.take();
String secondThreadResponse = future.get();
totalProcessingTime
= System.currentTimeMillis() - startProcessingTime;

assertTrue(
"Last response should be from the slow thread",
"slow thread".equals(secondThreadResponse));
assertTrue(
totalProcessingTime >= 3000
&& totalProcessingTime < 4000);
LOG.debug("Thread finished after: " + totalProcessingTime
29
Advanced Concurrency in Java

+ " milliseconds");

awaitTerminationAfterShutdown(WORKER_THREAD_POOL);

6. Conclusion

Depending on the use case, we have various options to wait for threads to finish their
execution.
A CountDownLatch is useful when we need a mechanism to notify one or more threads
that a set of operations performed by other threads has finished.
ExecutorCompletionService is useful when we need to access the task result as soon as
possible and other approaches when we want to wait for all of the running tasks to
finish.
30
Advanced Concurrency in Java

Guide to the Java Phaser

1. Overview

In this article, we will be looking at the Phaser  construct from


the java.util.concurrent package. It is a very similar construct to the CountDownLatch that
allows us to coordinate execution of threads. In comparison to the CountDownLatch, it has
some additional functionality.
The Phaser is a barrier on which the dynamic number of threads need to wait before
continuing execution. In the CountDownLatch that number cannot be configured
dynamically and needs to be supplied when we're creating the instance.

2. Phaser API

The Phaser allows us to build logic in which threads need to wait on the barrier before


going to the next step of execution.
We can coordinate multiple phases of execution, reusing a Phaser instance for each program
phase. Each phase can have a different number of threads waiting for advancing to another
phase. We'll have a look at an example of using phases later on.
To participate in the coordination, the thread needs to register() itself with
the Phaser instance. Note that this only increases the number of registered parties, and we
can't check whether the current thread is registered – we'd have to subclass the
implementation to supports this.
The thread signals that it arrived at the barrier by calling the arriveAndAwaitAdvance(),
which is a blocking method. When the number of arrived parties is equal to the number
of registered parties, the execution of the program will continue, and the phase number
will increase. We can get the current phase number by calling the getPhase() method.
When the thread finishes its job, we should call the arriveAndDeregister() method to signal
that the current thread should no longer be accounted for in this particular phase.

3. Implementing Logic Using Phaser API

Let's say that we want to coordinate multiple phases of actions. Three threads will process the
first phase, and two threads will process the second phase.
We'll create a LongRunningAction  class that implements the Runnable interface:
class LongRunningAction implements Runnable {
private String threadName;
private Phaser ph;

LongRunningAction(String threadName, Phaser ph) {


this.threadName = threadName;
31
Advanced Concurrency in Java

this.ph = ph;
ph.register();
}

@Override
public void run() {
ph.arriveAndAwaitAdvance();
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
ph.arriveAndDeregister();
}
}
When our action class is instantiated, we're registering to the Phaser instance using
the register() method. This will increment the number of threads using that specific Phaser.
The call to the arriveAndAwaitAdvance() will cause the current thread to wait on the barrier.
As already mentioned, when the number of arrived parties becomes the same as the number
of registered parties, the execution will continue.
After the processing is done, the current thread is deregistering itself by calling
the arriveAndDeregister()  method.
Let's create a test case in which we will start three LongRunningAction threads and block on
the barrier. Next, after the action is finished, we will create two
additional LongRunningAction threads that will perform processing of the next phase.
When creating Phaser instance from the main thread, we're passing 1 as an argument. This is
equivalent to calling the register() method from the current thread. We're doing this because,
when we're creating three worker threads, the main thread is a coordinator, and therefore
the Phaser needs to have four threads registered to it:
ExecutorService executorService = Executors.newCachedThreadPool();
Phaser ph = new Phaser(1);

assertEquals(0, ph.getPhase());
The phase after the initialization is equal to zero.
The Phaser class has a constructor in which we can pass a parent instance to it. It is useful in
cases where we have large numbers of parties that would experience massive synchronization
contention costs. In such situations, instances of Phasers may be set up so that groups of sub-
phasers share a common parent.
Next, let's start three LongRunningAction action threads, which will be waiting on the barrier
until we will call the arriveAndAwaitAdvance() method from the main thread.
Keep in mind we've initialized our Phaser with 1 and called register() three more times.
Now, three action threads have announced that they've arrived at the barrier, so one more call
of arriveAndAwaitAdvance() is needed – the one from the main thread:
executorService.submit(new LongRunningAction("thread-1", ph));
32
Advanced Concurrency in Java

executorService.submit(new LongRunningAction("thread-2", ph));


executorService.submit(new LongRunningAction("thread-3", ph));

ph.arriveAndAwaitAdvance();

assertEquals(1, ph.getPhase());
After the completion of that phase, the getPhase() method will return one because the
program finished processing the first step of execution.
Let's say that two threads should conduct the next phase of processing. We can
leverage Phaser to achieve that because it allows us to configure dynamically the number of
threads that should wait on the barrier. We're starting two new threads, but these will not
proceed to execute until the call to the arriveAndAwaitAdvance() from the main thread (same
as in the previous case):
executorService.submit(new LongRunningAction("thread-4", ph));
executorService.submit(new LongRunningAction("thread-5", ph));
ph.arriveAndAwaitAdvance();

assertEquals(2, ph.getPhase());

ph.arriveAndDeregister();
After this, the getPhase() method will return phase number equal to two. When we want to
finish our program, we need to call the arriveAndDeregister() method as the main thread is
still registered in the Phaser. When the deregistration causes the number of registered parties
to become zero, the Phaser is terminated. All calls to synchronization methods will not block
anymore and will return immediately.
Running the program will produce the following output (full source code with the print line
statements can be found in the code repository):
This is phase 0
This is phase 0
This is phase 0
Thread thread-2 before long running action
Thread thread-1 before long running action
Thread thread-3 before long running action
This is phase 1
This is phase 1
Thread thread-4 before long running action
Thread thread-5 before long running action
We see that all threads are waiting for execution until the barrier opens. Next phase of the
execution is performed only when the previous one finished successfully.

4. Conclusion

In this tutorial, we had a look at the Phaser  construct from java.util.concurrent and we


implemented the coordination logic with multiple phases using Phaser class.
33
Advanced Concurrency in Java
34
Advanced Concurrency in Java

Guide To CompletableFuture

1. Introduction

This tutorial is a guide to the functionality and use cases of the CompletableFuture class that
was introduced as a Java 8 Concurrency API improvement.

2. Asynchronous Computation in Java

Asynchronous computation is difficult to reason about. Usually we want to think of any


computation as a series of steps, but in the case of asynchronous computation, actions
represented as callbacks tend to be either scattered across the code or deeply nested
inside each other. Things get even worse when we need to handle errors that might occur
during one of the steps.
The Future interface was added in Java 5 to serve as a result of an asynchronous
computation, but it did not have any methods to combine these computations or handle
possible errors.
Java 8 introduced the CompletableFuture class. Along with the Future interface, it also
implemented the CompletionStage interface. This interface defines the contract for an
asynchronous computation step that we can combine with other steps.
CompletableFuture is at the same time a building block and a framework, with about 50
different methods for composing, combining, and executing asynchronous computation
steps and handling errors.
Such a large API can be overwhelming, but these mostly fall in several clear and distinct use
cases.

3. Using CompletableFuture as a Simple Future

First of all, the CompletableFuture class implements the Future interface, so we can use it as


a Future implementation, but with additional completion logic.
For example, we can create an instance of this class with a no-arg constructor to represent
some future result, hand it out to the consumers, and complete it at some time in the future
using the complete method. The consumers may use the get method to block the current
thread until this result is provided.
In the example below, we have a method that creates a CompletableFuture instance, then
spins off some computation in another thread and returns the Future immediately.
When the computation is done, the method completes the Future by providing the result to
the complete method:
public Future<String> calculateAsync() throws InterruptedException {
CompletableFuture<String> completableFuture = new CompletableFuture<>();
35
Advanced Concurrency in Java

Executors.newCachedThreadPool().submit(() -> {
Thread.sleep(500);
completableFuture.complete("Hello");
return null;
});

return completableFuture;
}
To spin off the computation, we use the Executor API. This method of creating and
completing a CompletableFuture can be used together with any concurrency mechanism or
API, including raw threads.
Notice that the calculateAsync method returns a Future instance.
We simply call the method, receive the Future instance, and call the get method on it when
we're ready to block for the result.
Also observe that the get method throws some checked exceptions,
namely ExecutionException (encapsulating an exception that occurred during a computation)
and InterruptedException (an exception signifying that a thread executing a method was
interrupted):
Future<String> completableFuture = calculateAsync();

// ...

String result = completableFuture.get();


assertEquals("Hello", result);
If we already know the result of a computation, we can use the
static completedFuture method with an argument that represents a result of this computation.
Consequently, the get method of the Future will never block, immediately returning this
result instead:
Future<String> completableFuture =
CompletableFuture.completedFuture("Hello");

// ...

String result = completableFuture.get();


assertEquals("Hello", result);
As an alternative scenario, we may want to cancel the execution of a Future.

4. CompletableFuture with Encapsulated Computation Logic

The code above allows us to pick any mechanism of concurrent execution, but what if we
want to skip this boilerplate and simply execute some code asynchronously?
Static methods runAsync and supplyAsync allow us to create a CompletableFuture instance
out of Runnable and Supplier functional types correspondingly.
36
Advanced Concurrency in Java

Both Runnable and Supplier are functional interfaces that allow passing their instances as


lambda expressions thanks to the new Java 8 feature.
The Runnable interface is the same old interface that is used in threads and it does not allow
to return a value.
The Supplier interface is a generic functional interface with a single method that has no
arguments and returns a value of a parameterized type.
This allows us to provide an instance of the Supplier as a lambda expression that does
the calculation and returns the result. It is as simple as:
CompletableFuture<String> future
= CompletableFuture.supplyAsync(() -> "Hello");

// ...

assertEquals("Hello", future.get());

5. Processing Results of Asynchronous Computations

The most generic way to process the result of a computation is to feed it to a function.
The thenApply method does exactly that; it accepts a Function instance, uses it to process the
result, and returns a Future that holds a value returned by a function:
CompletableFuture<String> completableFuture
= CompletableFuture.supplyAsync(() -> "Hello");

CompletableFuture<String> future = completableFuture


.thenApply(s -> s + " World");

assertEquals("Hello World", future.get());


If we don't need to return a value down the Future chain, we can use an instance of
the Consumer functional interface. Its single method takes a parameter and returns void.
There's a method for this use case in the CompletableFuture. The thenAccept method
receives a Consumer and passes it the result of the computation. Then the
final future.get() call returns an instance of the Void type:
CompletableFuture<String> completableFuture
= CompletableFuture.supplyAsync(() -> "Hello");

CompletableFuture<Void> future = completableFuture


.thenAccept(s -> System.out.println("Computation returned: " + s));

future.get();
Finally, if we neither need the value of the computation, nor want to return some value at the
end of the chain, then we can pass a Runnable lambda to the thenRun method. In the
following example, we simply print a line in the console after calling the future.get():
CompletableFuture<String> completableFuture
37
Advanced Concurrency in Java

= CompletableFuture.supplyAsync(() -> "Hello");

CompletableFuture<Void> future = completableFuture


.thenRun(() -> System.out.println("Computation finished."));

future.get();

6. Combining Futures

The best part of the CompletableFuture API is the ability to


combine CompletableFuture instances in a chain of computation steps.
The result of this chaining is itself a CompletableFuture that allows further chaining and
combining. This approach is ubiquitous in functional languages and is often referred to as a
monadic design pattern.
In the following example we use the thenCompose method to chain
two Futures sequentially.
Notice that this method takes a function that returns a CompletableFuture instance. The
argument of this function is the result of the previous computation step. This allows us to use
this value inside the next CompletableFuture‘s lambda:
CompletableFuture<String> completableFuture
= CompletableFuture.supplyAsync(() -> "Hello")
.thenCompose(s -> CompletableFuture.supplyAsync(() -> s + " World"));

assertEquals("Hello World", completableFuture.get());


The thenCompose method, together with thenApply, implement basic building blocks of the
monadic pattern. They closely relate to the map and flatMap methods
of Stream and Optional classes also available in Java 8.
Both methods receive a function and apply it to the computation result, but
the thenCompose (flatMap) method receives a function that returns another object of the
same type. This functional structure allows composing the instances of these classes as
building blocks.
If we want to execute two independent Futures and do something with their results, we can
use the thenCombine method that accepts a Future and a Function with two arguments to
process both results:
CompletableFuture<String> completableFuture
= CompletableFuture.supplyAsync(() -> "Hello")
.thenCombine(CompletableFuture.supplyAsync(
() -> " World"), (s1, s2) -> s1 + s2));

assertEquals("Hello World", completableFuture.get());


A simpler case is when we want to do something with two Futures‘ results, but don't need to
pass any resulting value down a Future chain. The thenAcceptBoth method is there to help:
CompletableFuture future = CompletableFuture.supplyAsync(() -> "Hello")
38
Advanced Concurrency in Java

.thenAcceptBoth(CompletableFuture.supplyAsync(() -> " World"),


(s1, s2) -> System.out.println(s1 + s2));

7. Difference Between thenApply() and thenCompose()

In our previous sections, we've shown examples regarding thenApply() and thenCompose().


Both APIs help chain different CompletableFuture calls, but the usage of these 2 functions is
different.

7.1. thenApply()

We can use this method to work with a result of the previous call. However, a key point
to remember is that the return type will be combined of all calls.
So this method is useful when we want to transform the result of a CompletableFuture call:
CompletableFuture<Integer> finalResult = compute().thenApply(s-> s + 1);

7.2. thenCompose()

The thenCompose() method is similar to thenApply() in that both return a new Completion


Stage. However, thenCompose() uses the previous stage as the argument. It will flatten
and return a Future with the result directly, rather than a nested future as we observed
in thenApply():
CompletableFuture<Integer> computeAnother(Integer i){
return CompletableFuture.supplyAsync(() -> 10 + i);
}
CompletableFuture<Integer> finalResult =
compute().thenCompose(this::computeAnother);
So if the idea is to chain CompletableFuture methods then it’s better to use thenCompose().
Also, note that the difference between these two methods is analogous to the difference
between map() and flatMap().

8. Running Multiple Futures in Parallel

When we need to execute multiple Futures in parallel, we usually want to wait for all of them
to execute and then process their combined results.
The CompletableFuture.allOf static method allows to wait for completion of all of
the Futures provided as a var-arg:
CompletableFuture<String> future1
= CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2
= CompletableFuture.supplyAsync(() -> "Beautiful");
39
Advanced Concurrency in Java

CompletableFuture<String> future3
= CompletableFuture.supplyAsync(() -> "World");

CompletableFuture<Void> combinedFuture
= CompletableFuture.allOf(future1, future2, future3);

// ...

combinedFuture.get();

assertTrue(future1.isDone());
assertTrue(future2.isDone());
assertTrue(future3.isDone());
Notice that the return type of the CompletableFuture.allOf() is a CompletableFuture<Void>.
The limitation of this method is that it does not return the combined results of all Futures.
Instead, we have to manually get results from Futures.
Fortunately, CompletableFuture.join() method and Java 8 Streams API makes it simple:
String combined = Stream.of(future1, future2, future3)
.map(CompletableFuture::join)
.collect(Collectors.joining(" "));

assertEquals("Hello Beautiful World", combined);


The CompletableFuture.join() method is similar to the get method, but it throws an
unchecked exception in case the Future does not complete normally. This makes it possible
to use it as a method reference in the Stream.map() method.

9. Handling Errors

For error handling in a chain of asynchronous computation steps, we have to adapt


the throw/catch idiom in a similar fashion.
Instead of catching an exception in a syntactic block, the CompletableFuture class allows us
to handle it in a special handle method. This method receives two parameters: a result of a
computation (if it finished successfully), and the exception thrown (if some computation step
did not complete normally).
In the following example, we use the handle method to provide a default value when the
asynchronous computation of a greeting was finished with an error because no name was
provided:
String name = null;

// ...

CompletableFuture<String> completableFuture
= CompletableFuture.supplyAsync(() -> {
if (name == null) {
throw new RuntimeException("Computation error!");
40
Advanced Concurrency in Java

}
return "Hello, " + name;
})}).handle((s, t) -> s != null ? s : "Hello, Stranger!");

assertEquals("Hello, Stranger!", completableFuture.get());


As an alternative scenario, suppose we want to manually complete the Future with a value, as
in the first example, but also have the ability to complete it with an exception.
The completeExceptionally method is intended for just that.
The completableFuture.get() method in the following example throws
an ExecutionException with a RuntimeException as its cause:
CompletableFuture<String> completableFuture = new CompletableFuture<>();

// ...

completableFuture.completeExceptionally(
new RuntimeException("Calculation failed!"));

// ...

completableFuture.get(); // ExecutionException
In the example above, we could have handled the exception with the handle method
asynchronously, but with the get method we can use the more typical approach of a
synchronous exception processing.

10. Async Methods

Most methods of the fluent API in CompletableFuture class have two additional variants with
the Async postfix. These methods are usually intended for running a corresponding step of
execution in another thread.
The methods without the Async postfix run the next execution stage using a calling thread. In
contrast, the Async method without the Executor argument runs a step using the
common fork/join pool implementation of Executor that is accessed with
the ForkJoinPool.commonPool() method. Finally, the Async method with
an Executor argument runs a step using the passed Executor.
Here's a modified example that processes the result of a computation with
a Function instance. The only visible difference is the thenApplyAsync method, but under the
hood the application of a function is wrapped into a ForkJoinTask instance (for more
information on the fork/join framework, see the article “Guide to the Fork/Join Framework in
Java”). This allows us to parallelize our computation even more and use system resources
more efficiently:
CompletableFuture<String> completableFuture
= CompletableFuture.supplyAsync(() -> "Hello");

CompletableFuture<String> future = completableFuture


.thenApplyAsync(s -> s + " World");
41
Advanced Concurrency in Java

assertEquals("Hello World", future.get());

11. JDK 9 CompletableFuture API

Java 9 enhances the CompletableFuture API with the following changes:

 New factory methods added


 Support for delays and timeouts
 Improved support for subclassing

and new instance APIs:

 Executor defaultExecutor()
 CompletableFuture<U> newIncompleteFuture()
 CompletableFuture<T> copy()
 CompletionStage<T> minimalCompletionStage()
 CompletableFuture<T> completeAsync(Supplier<? extends T> supplier, Executor
executor)
 CompletableFuture<T> completeAsync(Supplier<? extends T> supplier)
 CompletableFuture<T> orTimeout(long timeout, TimeUnit unit)
 CompletableFuture<T> completeOnTimeout(T value, long timeout, TimeUnit unit)

We also now have a few static utility methods:

 Executor delayedExecutor(long delay, TimeUnit unit, Executor executor)


 Executor delayedExecutor(long delay, TimeUnit unit)
 <U> CompletionStage<U> completedStage(U value)
 <U> CompletionStage<U> failedStage(Throwable ex)
 <U> CompletableFuture<U> failedFuture(Throwable ex)

Finally, to address timeout, Java 9 has introduced two more new functions:

 orTimeout()
 completeOnTimeout()

Here's the detailed article for further reading: Java 9 CompletableFuture API Improvements.
42
Advanced Concurrency in Java

CyclicBarrier in Java

1. Introduction

CyclicBarriers are synchronization constructs that were introduced with Java 5 as a part of


the java.util.concurrent package.
In this article, we'll explore this implementation in a concurrency scenario.

2. Java Concurrency – Synchronizers

The java.util.concurrent package contains several classes that help manage a set of threads


that collaborate with each other. Some of these include:

 CyclicBarrier
 Phaser
 CountDownLatch
 Exchanger
 Semaphore
 SynchronousQueue

These classes offer out of the box functionality for common interaction patterns between
threads.
If we have a set of threads that communicate with each other and resemble one of the
common patterns, we can simply reuse the appropriate library classes (also
called Synchronizers) instead of trying to come up with a custom scheme using a set of
locks and condition objects and the synchronized keyword.
Let's focus on the CyclicBarrier  going forward.

3. CyclicBarrier

A CyclicBarrier is a synchronizer that allows a set of threads to wait for each other to reach a
common execution point, also called a barrier.

CyclicBarriers are used in programs in which we have a fixed number of threads that must

wait for each other to reach a common point before continuing execution.
The barrier is called cyclic because it can be re-used after the waiting threads are
released.
43
Advanced Concurrency in Java

4. Usage

The constructor for a CyclicBarrier is simple. It takes a single integer that denotes the
number of threads that need to call the await() method on the barrier instance to signify
reaching the common execution point:
public CyclicBarrier(int parties)
The threads that need to synchronize their execution are also called parties and calling
the await() method is how we can register that a certain thread has reached the barrier point.
This call is synchronous and the thread calling this method suspends execution till a specified
number of threads have called the same method on the barrier. This situation where the
required number of threads have called await(), is called tripping the barrier.
Optionally, we can pass the second argument to the constructor, which is
a Runnable instance. This has logic that would be run by the last thread that trips the barrier:
public CyclicBarrier(int parties, Runnable barrierAction)

5. Implementation

To see CyclicBarrier in action, let's consider the following scenario:


There's an operation that a fixed number of threads perform and store the corresponding
results in a list. When all threads finish performing their action, one of them (typically the last
one that trips the barrier) starts processing the data that was fetched by each of these.
Let's implement the main class where all the action happens:
public class CyclicBarrierDemo {

private CyclicBarrier cyclicBarrier;


private List<List<Integer>> partialResults
= Collections.synchronizedList(new ArrayList<>());
private Random random = new Random();
private int NUM_PARTIAL_RESULTS;
private int NUM_WORKERS;

// ...
}
This class is pretty straight forward – NUM_WORKERS is the number of threads that are
going to execute and NUM_PARTIAL_RESULTS is the number of results that each of the
worker threads is going to produce.
Finally, we have partialResults that are a list that's going to store the results of each of these
worker threads. Do note that this list is a SynchronizedList because multiple threads will be
writing to it at the same time, and the add() method isn't thread-safe on a plain ArrayList.
Now let's implement the logic of each of the worker threads:
public class CyclicBarrierDemo {
44
Advanced Concurrency in Java

// ...

class NumberCruncherThread implements Runnable {

@Override
public void run() {
String thisThreadName = Thread.currentThread().getName();
List<Integer> partialResult = new ArrayList<>();

// Crunch some numbers and store the partial result


for (int i = 0; i < NUM_PARTIAL_RESULTS; i++) {
Integer num = random.nextInt(10);
System.out.println(thisThreadName
+ ": Crunching some numbers! Final result - " + num);
partialResult.add(num);
}

partialResults.add(partialResult);
try {
System.out.println(thisThreadName
+ " waiting for others to reach barrier.");
cyclicBarrier.await();
} catch (InterruptedException e) {
// ...
} catch (BrokenBarrierException e) {
// ...
}
}
}

}
We'll now implement the logic that runs when the barrier has been tripped.
To keep things simple, let's just add all the numbers in the partial results list:
public class CyclicBarrierDemo {

// ...

class AggregatorThread implements Runnable {

@Override
public void run() {

String thisThreadName = Thread.currentThread().getName();

System.out.println(
thisThreadName + ": Computing sum of " + NUM_WORKERS
45
Advanced Concurrency in Java

+ " workers, having " + NUM_PARTIAL_RESULTS + " results each.");


int sum = 0;

for (List<Integer> threadResult : partialResults) {


System.out.print("Adding ");
for (Integer partialResult : threadResult) {
System.out.print(partialResult+" ");
sum += partialResult;
}
System.out.println();
}
System.out.println(thisThreadName + ": Final result = " + sum);
}
}
}
The final step would be to construct the CyclicBarrier and kick things off with
a main() method:
public class CyclicBarrierDemo {

// Previous code

public void runSimulation(int numWorkers, int numberOfPartialResults) {


NUM_PARTIAL_RESULTS = numberOfPartialResults;
NUM_WORKERS = numWorkers;

cyclicBarrier = new CyclicBarrier(NUM_WORKERS, new


AggregatorThread());

System.out.println("Spawning " + NUM_WORKERS


+ " worker threads to compute "
+ NUM_PARTIAL_RESULTS + " partial results each");

for (int i = 0; i < NUM_WORKERS; i++) {


Thread worker = new Thread(new NumberCruncherThread());
worker.setName("Thread " + i);
worker.start();
}
}

public static void main(String[] args) {


CyclicBarrierDemo demo = new CyclicBarrierDemo();
demo.runSimulation(5, 3);
}
}
In the above code, we initialized the cyclic barrier with 5 threads that each produce 3 integers
as a part of their computation and store the same in the resulting list.
46
Advanced Concurrency in Java

Once the barrier is tripped, the last thread that tripped the barrier executes the logic specified
in the AggregatorThread, namely – add all the numbers produced by the threads.

6. Results

Here is the output from one execution of the above program – each execution might create
different results as the threads can be spawned in a different order:
Spawning 5 worker threads to compute 3 partial results each
Thread 0: Crunching some numbers! Final result - 6
Thread 0: Crunching some numbers! Final result - 2
Thread 0: Crunching some numbers! Final result - 2
Thread 0 waiting for others to reach barrier.
Thread 1: Crunching some numbers! Final result - 2
Thread 1: Crunching some numbers! Final result - 0
Thread 1: Crunching some numbers! Final result - 5
Thread 1 waiting for others to reach barrier.
Thread 3: Crunching some numbers! Final result - 6
Thread 3: Crunching some numbers! Final result - 4
Thread 3: Crunching some numbers! Final result - 0
Thread 3 waiting for others to reach barrier.
Thread 2: Crunching some numbers! Final result - 1
Thread 2: Crunching some numbers! Final result - 1
Thread 2: Crunching some numbers! Final result - 0
Thread 2 waiting for others to reach barrier.
Thread 4: Crunching some numbers! Final result - 9
Thread 4: Crunching some numbers! Final result - 3
Thread 4: Crunching some numbers! Final result - 5
Thread 4 waiting for others to reach barrier.
Thread 4: Computing final sum of 5 workers, having 3 results each.
Adding 6 2 2
Adding 2 0 5
Adding 6 4 0
Adding 1 1 0
Adding 9 3 5
Thread 4: Final result = 46
As the above output shows, Thread 4 is the one that trips the barrier and also executes the
final aggregation logic. It is also not necessary that threads are actually run in the order that
they're started as the above example shows.

7. Conclusion

In this article, we saw what a CyclicBarrier is, and what kind of situations it is helpful in.
We also implemented a scenario where we needed a fixed number of threads to reach a fixed
execution point, before continuing with other program logic.
47
Advanced Concurrency in Java
48
Advanced Concurrency in Java

Guide to ThreadLocalRandom in Java

1. Overview

Generating random values is a very common task. This is why Java provides
the java.util.Random class.
However, this class doesn't perform well in a multi-threaded environment.
In a simplified way, the reason for the poor performance of Random in a multi-threaded
environment is due to contention – given that multiple threads share the
same Random instance.
To address that limitation, Java introduced
the java.util.concurrent.ThreadLocalRandom class in JDK 7 – for generating random
numbers in a multi-threaded environment.
Let's see how ThreadLocalRandom performs and how to use it in real-world applications.

2. ThreadLocalRandom Over Random

ThreadLocalRandom is a combination of the ThreadLocal and Random classes (more on


this later) and is isolated to the current thread. Thus, it achieves better performance in a
multithreaded environment by simply avoiding any concurrent access to instances
of Random.
The random number obtained by one thread is not affected by the other thread,
whereas java.util.Random provides random numbers globally.
Also, unlike Random, ThreadLocalRandom doesn't support setting the seed explicitly.
Instead, it overrides the setSeed(long seed) method inherited from Random to always throw
an UnsupportedOperationException if called.

2.1. Thread Contention

So far, we've established that the Random class performs poorly in highly concurrent


environments. To better understand this, let's see how one of its primary operations, next(int),
is implemented:
private final AtomicLong seed;

protected int next(int bits) {


long oldseed, nextseed;
AtomicLong seed = this.seed;
do {
oldseed = seed.get();
nextseed = (oldseed * multiplier + addend) & mask;
} while (!seed.compareAndSet(oldseed, nextseed));
49
Advanced Concurrency in Java

return (int)(nextseed >>> (48 - bits));


}
This is a Java implementation for the Linear Congruential Generator algorithm. It's obvious
that all threads are sharing the same seed instance variable.
To generate the next random set of bits, it first tries to change the shared seed value
atomically via compareAndSet or CAS for short.
When multiple threads attempt to update the seed concurrently using CAS, one thread
wins and updates the seed,  and the rest lose. Losing threads will try the same process
over and over again until they get a chance to update the value and ultimately generate
the random number.
This algorithm is lock-free, and different threads can progress concurrently. However, when
the contention is high, the number of CAS failures and retries will hurt the overall
performance significantly.
On the other hand, the ThreadLocalRandom completely removes this contention, as each
thread has its own instance of Random  and, consequently, its own confined seed.
Let's now take a look at some of the ways to generate random int, long and double values.

3. Generating Random Values Using ThreadLocalRandom

As per the Oracle documentation, we just need to


call ThreadLocalRandom.current() method, and it will return the instance
of ThreadLocalRandom for the current thread. We can then generate random values by
invoking available instance methods of the class.
Let's generate a random int value without any bounds:
int unboundedRandomValue = ThreadLocalRandom.current().nextInt());
Next, let's see how we can generate a random bounded int value, meaning a value between a
given lower and upper limit.
Here's an example of generating a random int value between 0 and 100:
int boundedRandomValue = ThreadLocalRandom.current().nextInt(0, 100);
Please note, 0 is the inclusive lower limit and 100 is the exclusive upper limit.
We can generate random values for long and double by
invoking nextLong() and nextDouble() methods in a similar way as shown in the examples
above.
Java 8 also adds the nextGaussian() method to generate the next normally-distributed value
with a 0.0 mean and 1.0 standard deviation from the generator's sequence.
As with the Random class, we can also use the doubles(), ints() and longs() methods to
generate streams of random values.
50
Advanced Concurrency in Java

4. Comparing ThreadLocalRandom and Random Using JMH

Let's see how we can generate random values in a multi-threaded environment, by using the
two classes, then compare their performance using JMH.
First, let's create an example where all the threads are sharing a single instance
of Random. Here, we're submitting the task of generating a random value using
the Random instance to an ExecutorService:
ExecutorService executor = Executors.newWorkStealingPool();
List<Callable<Integer>> callables = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < 1000; i++) {
callables.add(() -> {
return random.nextInt();
});
}
executor.invokeAll(callables);
Let's check the performance of the code above using JMH benchmarking:
# Run complete. Total time: 00:00:36
Benchmark Mode Cnt Score Error Units
ThreadLocalRandomBenchMarker.randomValuesUsingRandom avgt 20 771.613 ±
222.220 us/op
Similarly, let's now use ThreadLocalRandom instead of the Random instance, which uses one
instance of ThreadLocalRandom for each thread in the pool:
ExecutorService executor = Executors.newWorkStealingPool();
List<Callable<Integer>> callables = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
callables.add(() -> {
return ThreadLocalRandom.current().nextInt();
});
}
executor.invokeAll(callables);
Here's the result of using ThreadLocalRandom:
# Run complete. Total time: 00:00:36
Benchmark Mode Cnt Score Error Units
ThreadLocalRandomBenchMarker.randomValuesUsingThreadLocalRandom avgt 20
624.911 ± 113.268 us/op
Finally, by comparing the JMH results above for both Random and ThreadLocalRandom, we
can clearly see that the average time taken to generate 1000 random values using Random is
772 microseconds, whereas using ThreadLocalRandom it's around 625 microseconds.
Thus, we can conclude that ThreadLocalRandom is more efficient in a highly concurrent
environment.
To learn more about JMH, check out our previous article here.
51
Advanced Concurrency in Java

5. Implementation Details

It's a good mental model to think of a ThreadLocalRandom as a combination


of ThreadLocal and Random classes. As a matter of fact, this mental model was aligned with
the actual implementation before Java 8.
As of Java 8, however, this alignment broke down completely as
the ThreadLocalRandom  became a singleton. Here's how the current() method looks in
Java 8+:
static final ThreadLocalRandom instance = new ThreadLocalRandom();

public static ThreadLocalRandom current() {


if (U.getInt(Thread.currentThread(), PROBE) == 0)
localInit();

return instance;
}
It's true that sharing one global Random instance leads to sub-optimal performance in high
contention. However, using one dedicated instance per thread is also overkill.
Instead of a dedicated instance of Random per thread, each thread only needs to
maintain its own seed value. As of Java 8, the Thread class itself has been retrofitted to
maintain the seed value:
public class Thread implements Runnable {
// omitted

@jdk.internal.vm.annotation.Contended("tlr")
long threadLocalRandomSeed;

@jdk.internal.vm.annotation.Contended("tlr")
int threadLocalRandomProbe;

@jdk.internal.vm.annotation.Contended("tlr")
int threadLocalRandomSecondarySeed;
}
The threadLocalRandomSeed variable is responsible for maintaining the current seed value
for ThreadLocalRandom.  Moreover, the secondary seed, threadLocalRandomSecondarySeed,
is usually used internally by the likes of ForkJoinPool.
This implementation incorporates a few optimizations to make ThreadLocalRandom even
more performant:

 Avoiding false sharing by using the @Contented annotation, which basically adds


enough padding to isolate the contended variables in their own cache lines
 Using sun.misc.Unsafe to update these three variables instead of using the Reflection
API
 Avoiding extra hashtable lookups associated with the ThreadLocal implementation
52
Advanced Concurrency in Java

6. Conclusion

This article illustrated the difference


between java.util.Random and java.util.concurrent.ThreadLocalRandom.
We also saw the advantage of ThreadLocalRandom over Random in a multithreaded
environment, as well as performance and how we can generate random values using the class.
ThreadLocalRandom is a simple addition to the JDK, but it can create a notable impact in
highly concurrent applications.
53
Advanced Concurrency in Java

Java CyclicBarrier vs CountDownLatch

1. Introduction

In this tutorial, we'll compare CyclicBarrier and CountDownLatch and try to understand the


similarities and differences between the two.

2. What Are These?

When it comes to concurrency, it can be challenging to conceptualize what each is intended


to accomplish.
First and foremost, both CountDownLatch and CyclicBarrier are used for managing
multi-threaded applications.
And, they are both intended to express how a given thread or group of threads should
wait.

2.1. CountDownLatch

A CountDownLatch is a construct that a thread waits on while other threads count down on


the latch until it reaches zero.
We can think of this like a dish at a restaurant that is being prepared. No matter which cook
prepares however many of the n items, the waiter must wait until all the items are on the
plate. If a plate takes n  items, any cook will count down on the latch for each item she puts
on the plate.

2.2. CyclicBarrier

A CyclicBarrier is a reusable construct where a group of threads waits together until all of


the threads arrive. At that point, the barrier is broken and an action can optionally be taken.
We can think of this like a group of friends. Every time they plan to eat at a restaurant they
decide a common point where they can meet. They wait for each other there, and only when
everyone arrives can they go to the restaurant to eat together.

2.3. Further Reading

And for a lot more detail on each of these individually, refer to our previous tutorials
on CountDownLatch and CyclicBarrier respectively.
54
Advanced Concurrency in Java

3. Tasks vs. Threads

Let's take a deeper dive into some of the semantic differences between these two classes.
As stated in the definitions, CyclicBarrier allows a number of threads to wait on each other,
whereas CountDownLatch allows one or more threads to wait for a number of tasks to
complete.
In short, CyclicBarrier maintains a count of threads whereas CountDownLatch maintains
a count of tasks.
In the following code, we define a CountDownLatch with a count of two. Next, we
call countDown() twice from a single thread:
CountDownLatch countDownLatch = new CountDownLatch(2);
Thread t = new Thread(() -> {
countDownLatch.countDown();
countDownLatch.countDown();
});
t.start();
countDownLatch.await();

assertEquals(0, countDownLatch.getCount());
Once the latch reaches zero, the call to await returns.
Note that in this case, we were able to have the same thread decrease the count twice.
CyclicBarrier, though, is different on this point.
Similar to the above example, we create a CyclicBarrier, again with a count of two and
call await() on it, this time from the same thread:
CyclicBarrier cyclicBarrier = new CyclicBarrier(2);
Thread t = new Thread(() -> {
try {
cyclicBarrier.await();
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
// error handling
}
});
t.start();

assertEquals(1, cyclicBarrier.getNumberWaiting());
assertFalse(cyclicBarrier.isBroken());
The first difference here is that the threads that are waiting are themselves the barrier.
Second, and more importantly, the second await() is useless. A single thread can't count
down  a barrier twice.
Indeed, because t must wait for another thread to call await() – to bring the count to two
– t‘s second call to await()  won't actually be invoked until the barrier is already broken!
55
Advanced Concurrency in Java

In our test, the barrier hasn't been crossed because we only have one thread waiting and
not the two threads that would be required for the barrier to be tripped. This is also
evident from the cyclicBarrier.isBroken() method, which returns false.

4. Reusability

The second most evident difference between these two classes is reusability. To
elaborate, when the barrier trips in CyclicBarrier, the count resets to its original
value. CountDownLatch is different because the count never resets.
In the given code, we define a CountDownLatch with count 7 and count it through 20
different calls:
CountDownLatch countDownLatch = new CountDownLatch(7);
ExecutorService es = Executors.newFixedThreadPool(20);
for (int i = 0; i < 20; i++) {
es.execute(() -> {
long prevValue = countDownLatch.getCount();
countDownLatch.countDown();
if (countDownLatch.getCount() != prevValue) {
outputScraper.add("Count Updated");
}
});
}
es.shutdown();

assertTrue(outputScraper.size() <= 7);


We observe that even though 20 different threads call countDown(), the count doesn't reset
once it reaches zero.
Similar to the above example, we define a CyclicBarrier with count 7 and wait on it from 20
different threads:
CyclicBarrier cyclicBarrier = new CyclicBarrier(7);

ExecutorService es = Executors.newFixedThreadPool(20);
for (int i = 0; i < 20; i++) {
es.execute(() -> {
try {
if (cyclicBarrier.getNumberWaiting() <= 0) {
outputScraper.add("Count Updated");
}
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
// error handling
}
});
}
56
Advanced Concurrency in Java

es.shutdown();

assertTrue(outputScraper.size() > 7);


In this case, we observe that the value decreases every time a new thread runs, by resetting to
the original value, once it reaches zero.

5. Conclusion

All in all, CyclicBarrier and CountDownLatch are both helpful tools for synchronization


between multiple threads. However, they are fundamentally different in terms of the
functionality they provide. Consider each carefully when determining which is right for the
job.
57
Advanced Concurrency in Java

What is Thread-Safety and How to Achieve it?

1. Overview

Java supports multithreading out of the box. This means that by running bytecode
concurrently in separate worker threads, the JVM is capable of improving application
performance.
Although multithreading is a powerful feature, it comes at a price. In multithreaded
environments, we need to write implementations in a thread-safe way. This means that
different threads can access the same resources without exposing erroneous behavior or
producing unpredictable results. This programming methodology is known as “thread-
safety”.
In this tutorial, we'll look at different approaches to achieve it.

2. Stateless Implementations

In most cases, errors in multithreaded applications are the result of incorrectly sharing state
between several threads.
Therefore, the first approach that we'll look at is to achieve thread-safety using stateless
implementations.
To better understand this approach, let's consider a simple utility class with a static method
that calculates the factorial of a number:
public class MathUtils {

public static BigInteger factorial(int number) {


BigInteger f = new BigInteger("1");
for (int i = 2; i <= number; i++) {
f = f.multiply(BigInteger.valueOf(i));
}
return f;
}
}
The factorial() method is a stateless deterministic function. Given a specific input, it
always produces the same output.
The method neither relies on external state nor maintains state at all. Hence, it's
considered to be thread-safe and can be safely called by multiple threads at the same time.
All threads can safely call the factorial() method and will get the expected result without
interfering with each other and without altering the output that the method generates for other
threads.
Therefore, stateless implementations are the simplest way to achieve thread-safety.
58
Advanced Concurrency in Java

3. Immutable Implementations

If we need to share state between different threads, we can create thread-safe classes by
making them immutable.
Immutability is a powerful, language-agnostic concept and it's fairly easy to achieve in Java.
To put it simply, a class instance is immutable when its internal state can't be modified
after it has been constructed.
The easiest way to create an immutable class in Java is by declaring all the
fields private and final and not providing setters:
public class MessageService {

private final String message;

public MessageService(String message) {


this.message = message;
}

// standard getter

}
A MessageService object is effectively immutable since its state can't change after its
construction. Hence, it's thread-safe.
Moreover, if MessageService were actually mutable, but multiple threads only have read-only
access to it, it's thread-safe as well.
Thus, immutability is just another way to achieve thread-safety.

4. Thread-Local Fields

In object-oriented programming (OOP), objects actually need to maintain state through fields
and implement behavior through one or more methods.
If we actually need to maintain state, we can create thread-safe classes that don't share
state between threads by making their fields thread-local.
We can easily create classes whose fields are thread-local by simply defining private fields
in Thread classes.
We could define, for instance, a Thread class that stores an array of integers:
public class ThreadA extends Thread {

private final List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

@Override
public void run() {
numbers.forEach(System.out::println);
}
59
Advanced Concurrency in Java

}
While another one might hold an array of strings:
public class ThreadB extends Thread {

private final List<String> letters = Arrays.asList("a", "b", "c", "d", "e", "f");

@Override
public void run() {
letters.forEach(System.out::println);
}
}
In both implementations, the classes have their own state, but it's not shared with other
threads. Thus, the classes are thread-safe.
Similarly, we can create thread-local fields by assigning ThreadLocal instances to a field.
Let's consider, for example, the following StateHolder class:
public class StateHolder {

private final String state;

// standard constructors / getter


}
We can easily make it a thread-local variable as follows:
public class ThreadState {

public static final ThreadLocal<StateHolder> statePerThread = new


ThreadLocal<StateHolder>() {

@Override
protected StateHolder initialValue() {
return new StateHolder("active");
}
};

public static StateHolder getState() {


return statePerThread.get();
}
}
Thread-local fields are pretty much like normal class fields, except that each thread that
accesses them via a setter/getter gets an independently initialized copy of the field so that
each thread has its own state.

5. Synchronized Collections

We can easily create thread-safe collections by using the set of synchronization wrappers
included within the collections framework.
60
Advanced Concurrency in Java

We can use, for instance, one of these synchronization wrappers to create a thread-safe


collection:
Collection<Integer> syncCollection = Collections.synchronizedCollection(new
ArrayList<>());
Thread thread1 = new Thread(() -> syncCollection.addAll(Arrays.asList(1, 2, 3, 4, 5,
6)));
Thread thread2 = new Thread(() -> syncCollection.addAll(Arrays.asList(7, 8, 9, 10,
11, 12)));
thread1.start();
thread2.start();
Let's keep in mind that synchronized collections use intrinsic locking in each method (we'll
look at intrinsic locking later).
This means that the methods can be accessed by only one thread at a time, while other
threads will be blocked until the method is unlocked by the first thread.
Thus, synchronization has a penalty in performance, due to the underlying logic of
synchronized access.

6. Concurrent Collections

Alternatively to synchronized collections, we can use concurrent collections to create thread-


safe collections.
Java provides the java.util.concurrent package, which contains several concurrent
collections, such as ConcurrentHashMap:
Map<String,String> concurrentMap = new ConcurrentHashMap<>();
concurrentMap.put("1", "one");
concurrentMap.put("2", "two");
concurrentMap.put("3", "three");
Unlike their synchronized counterparts, concurrent collections achieve thread-safety by
dividing their data into segments. In a ConcurrentHashMap, for instance, several threads
can acquire locks on different map segments, so multiple threads can access the Map at the
same time.
Concurrent collections are much more performant than synchronized collections, due to
the inherent advantages of concurrent thread access.
It's worth mentioning that synchronized and concurrent collections only make the
collection itself thread-safe and not the contents.

7. Atomic Objects

It's also possible to achieve thread-safety using the set of atomic classes that Java provides,
including AtomicInteger, AtomicLong, AtomicBoolean, and AtomicReference.
61
Advanced Concurrency in Java

Atomic classes allow us to perform atomic operations, which are thread-safe, without
using synchronization. An atomic operation is executed in one single machine level
operation.
To understand the problem this solves, let's look at the following Counter class:
public class Counter {

private int counter = 0;

public void incrementCounter() {


counter += 1;
}

public int getCounter() {


return counter;
}
}
Let's suppose that in a race condition, two threads access
the incrementCounter() method at the same time.
In theory, the final value of the counter field will be 2. But we just can't be sure about the
result, because the threads are executing the same code block at the same time and
incrementation is not atomic.
Let's create a thread-safe implementation of the Counter class by using
an AtomicInteger object:
public class AtomicCounter {

private final AtomicInteger counter = new AtomicInteger();

public void incrementCounter() {


counter.incrementAndGet();
}

public int getCounter() {


return counter.get();
}
}
This is thread-safe because, while incrementation, ++, takes more than one
operation, incrementAndGet is atomic.

8. Synchronized Methods

While the earlier approaches are very good for collections and primitives, we will at times
need greater control than that.
So, another common approach that we can use for achieving thread-safety is implementing
synchronized methods.
62
Advanced Concurrency in Java

Simply put, only one thread can access a synchronized method at a time while blocking
access to this method from other threads. Other threads will remain blocked until the first
thread finishes or the method throws an exception.
We can create a thread-safe version of incrementCounter() in another way by making it a
synchronized method:
public synchronized void incrementCounter() {
counter += 1;
}
We've created a synchronized method by prefixing the method signature with
the synchronized keyword.
Since one thread at a time can access a synchronized method, one thread will execute
the incrementCounter() method, and in turn, others will do the same. No overlapping
execution will occur whatsoever.
Synchronized methods rely on the use of “intrinsic locks” or “monitor locks”. An
intrinsic lock is an implicit internal entity associated with a particular class instance.
In a multithreaded context, the term monitor is just a reference to the role that the lock
performs on the associated object, as it enforces exclusive access to a set of specified
methods or statements.
When a thread calls a synchronized method, it acquires the intrinsic lock. After the
thread finishes executing the method, it releases the lock, hence allowing other threads to
acquire the lock and get access to the method.
We can implement synchronization in instance methods, static methods, and statements
(synchronized statements).

9. Synchronized Statements

Sometimes, synchronizing an entire method might be overkill if we just need to make a


segment of the method thread-safe.
To exemplify this use case, let's refactor the incrementCounter() method:
public void incrementCounter() {
// additional unsynced operations
synchronized(this) {
counter += 1; 
}
}
The example is trivial, but it shows how to create a synchronized statement. Assuming that
the method now performs a few additional operations, which don't require synchronization,
we only synchronized the relevant state-modifying section by wrapping it within
a synchronized block.
Unlike synchronized methods, synchronized statements must specify the object that provides
the intrinsic lock, usually the this reference.
63
Advanced Concurrency in Java

Synchronization is expensive, so with this option, we are able to only synchronize the
relevant parts of a method.

9.1. Other Objects as a Lock

We can slightly improve the thread-safe implementation of the Counter class by exploiting


another object as a monitor lock, instead of this.
Not only does this provide coordinated access to a shared resource in a multithreaded
environment, but also it uses an external entity to enforce exclusive access to the
resource:
public class ObjectLockCounter {

private int counter = 0;


private final Object lock = new Object();

public void incrementCounter() {


synchronized(lock) {
counter += 1;
}
}

// standard getter
}
We use a plain Object instance to enforce mutual exclusion. This implementation is slightly
better, as it promotes security at the lock level.
When using this  for intrinsic locking, an attacker could cause a deadlock by acquiring the
intrinsic lock and triggering a denial of service (DoS) condition.
On the contrary, when using other objects, that private entity is not accessible from the
outside. This makes it harder for an attacker to acquire the lock and cause a deadlock.

9.2. Caveats

Even though we can use any Java object as an intrinsic lock, we should avoid
using Strings for locking purposes:
public class Class1 {
private static final String LOCK = "Lock";

// uses the LOCK as the intrinsic lock


}

public class Class2 {


private static final String LOCK = "Lock";

// uses the LOCK as the intrinsic lock


64
Advanced Concurrency in Java

}
At first glance, it seems that these two classes are using two different objects as their lock.
However, because of string interning, these two “Lock” values may actually refer to the
same object on the string pool. That is, the Class1  and Class2 are sharing the same lock!
This, in turn, may cause some unexpected behaviors in concurrent contexts.
In addition to Strings, we should avoid using any cacheable or reusable objects
as intrinsic locks. For example, the Integer.valueOf() method caches small numbers.
Therefore, calling Integer.valueOf(1)  returns the same object even in different classes.

10. Volatile Fields

Synchronized methods and blocks are handy for addressing variable visibility problems
among threads. Even so, the values of regular class fields might be cached by the CPU.
Hence, consequent updates to a particular field, even if they're synchronized, might not be
visible to other threads.
To prevent this situation, we can use volatile class fields:
public class Counter {

private volatile int counter;

// standard constructors / getter

}
With the volatile keyword, we instruct the JVM and the compiler to store
the counter variable in the main memory. That way, we make sure that every time the JVM
reads the value of the counter variable, it will actually read it from the main memory, instead
of from the CPU cache. Likewise, every time the JVM writes to the counter variable, the
value will be written to the main memory.
Moreover, the use of a volatile variable ensures that all variables that are visible to a
given thread will be read from the main memory as well.
Let's consider the following example:
public class User {

private String name;


private volatile int age;

// standard constructors / getters

}
In this case, each time the JVM writes the age volatile variable to the main memory, it will
write the non-volatile name variable to the main memory as well. This assures that the latest
values of both variables are stored in the main memory, so consequent updates to the
variables will automatically be visible to other threads.
65
Advanced Concurrency in Java

Similarly, if a thread reads the value of a volatile variable, all the variables visible to the
thread will be read from the main memory too.
This extended guarantee that volatile variables provide is known as the full volatile
visibility guarantee.

11. Reentrant Locks

Java provides an improved set of Lock implementations, whose behavior is slightly more


sophisticated than the intrinsic locks discussed above.
With intrinsic locks, the lock acquisition model is rather rigid: one thread acquires the
lock, then executes a method or code block, and finally releases the lock, so other threads can
acquire it and access the method.
There's no underlying mechanism that checks the queued threads and gives priority access to
the longest waiting threads.
ReentrantLock instances allow us to do exactly that, hence preventing queued threads from
suffering some types of resource starvation:
public class ReentrantLockCounter {

private int counter;


private final ReentrantLock reLock = new ReentrantLock(true);

public void incrementCounter() {


reLock.lock();
try {
counter += 1;
} finally {
reLock.unlock();
}
}

// standard constructors / getter

}
The ReentrantLock constructor takes an optional fairness boolean parameter. When set
to true, and multiple threads are trying to acquire a lock, the JVM will give priority to the
longest waiting thread and grant access to the lock.

12. Read/Write Locks

Another powerful mechanism that we can use for achieving thread-safety is the use
of ReadWriteLock implementations.
A ReadWriteLock lock actually uses a pair of associated locks, one for read-only operations
and other for writing operations.
66
Advanced Concurrency in Java

As a result, it's possible to have many threads reading a resource, as long as there's no


thread writing to it. Moreover, the thread writing to the resource will prevent other
threads from reading it.
We can use a ReadWriteLock lock as follows:
public class ReentrantReadWriteLockCounter {

private int counter;


private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private final Lock readLock = rwLock.readLock();
private final Lock writeLock = rwLock.writeLock();

public void incrementCounter() {


writeLock.lock();
try {
counter += 1;
} finally {
writeLock.unlock();
}
}

public int getCounter() {


readLock.lock();
try {
return counter;
} finally {
readLock.unlock();
}
}

// standard constructors

13. Conclusion

In this article, we learned what thread-safety is in Java, and took an in-depth look at
different approaches for achieving it.
67
Advanced Concurrency in Java

How to Delay Code Execution in Java

1. Introduction

It is relatively common for Java programs to add a delay or pause in their operation. This can
be useful for task pacing or to pause execution until another task completes.
This tutorial will describe two ways to implement delays in Java.

2. A Thread-Based Approach

When a Java program runs, it spawns a process that runs on the host machine. This
process contains at least one thread – the main thread – in which the program runs.
Furthermore, Java enables multithreading, which enables applications to create new threads
that run in parallel, or asynchronously, to the main thread.

2.1. Using Thread.sleep

A quick and dirty way to pause in Java is to tell the current thread to sleep for a specified
amount of time. This can be done using Thread.sleep(milliseconds):
try {
Thread.sleep(secondsToSleep * 1000);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
It is good practice to wrap the sleep method in a try/catch block in case another thread
interrupts the sleeping thread. In this case, we catch the InterruptedException and
explicitly interrupt the current thread, so it can be caught later and handled. This is more
important in a multi-threaded program, but still good practice in a single-threaded program in
case we add other threads later.

2.2. Using TimeUnit.sleep

For better readability, we can use TimeUnit.XXX.sleep(y), where XXX is the time unit to


sleep for (SECONDS, MINUTES, etc.), and y is the number of that unit to sleep for. This
uses Thread.sleep behind the scenes. Here's an example of the TimeUnit syntax:
try {
TimeUnit.SECONDS.sleep(secondsToSleep);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
However, there are some disadvantages to using these thread-based methods:
68
Advanced Concurrency in Java

 The sleep times are not exactly precise, especially when using smaller time
increments like milliseconds and nanoseconds
 When used inside of loops, sleep will drift slightly between loop iterations due to
other code execution so the execution time could get imprecise after many iterations

3. An ExecutorService-Based Approach

Java provides the ScheduledExecutorService interface, which is a more robust and precise


solution. This interface can schedule code to run once after a specified delay or at fixed time
intervals.
To run a piece of code once after a delay, we can use the schedule method:
ScheduledExecutorService executorService =
Executors.newSingleThreadScheduledExecutor();

executorService.schedule(Classname::someTask, delayInSeconds,
TimeUnit.SECONDS);
The Classname::someTask part is where we specify the method that will run after the delay:

 someTask is the name of the method we want to execute


 Classname is the name of the class that contains the someTask method

To run a task at fixed time intervals, we can use the scheduleAtFixedRate method:


ScheduledExecutorService executorService =
Executors.newSingleThreadScheduledExecutor();

executorService.scheduleAtFixedRate(Classname::someTask, 0, delayInSeconds,
TimeUnit.SECONDS);
This will repeatedly call the someTask method, pausing for delayInSeconds between each
call.
Besides allowing more timing options, the ScheduledExecutorService method yields more
precise time intervals, since it prevents issues with drift.

4. Conclusion

In this article, we discussed two methods for creating delays in Java programs.

You might also like