You are on page 1of 18

JAVA CONCURRENCY

Java Concurrency
TABLE OF CONTENTS

Preface 1
Introduction 1
Basic Concepts 1
JMM "happens-before" Relationship 2
Threads and Runnable 3
Synchronization 5
The synchronized Keyword . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
wait()/notify()/notifyAll() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
The volatile Keyword 7
The ThreadLocal Class 7
Immutable Objects 8
Deadlock, Livelock and Thread Starvation 9
Deadlock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Overcoming Deadlock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Livelock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
Thread Starvation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
The java.util.concurrent Package 11
Executor & ExecutorService . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
Semaphor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
CountDownLatch. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
CyclicBarrier. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
Concurrent collections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Atomics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
Locks. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

JAVACODEGEEKS.COM | © EXELIXIS MEDIA P.C. VISIT JAVACODEGEEKS.COM FOR MORE!


1 JAVA CONCURRENCY

PREFACE
PREFACE Concept Description

This Java Concurrency Cheatsheet is crafted with Thread A thread represents an


the intention of providing developers, both novice independent path of
and experienced, with a concise yet comprehensive execution within a Java
resource to navigate the intricacies of concurrent program. Threads allow
programming in Java. Whether you are just for concurrent and
beginning your journey into concurrent parallel execution of
programming or seeking to refine your existing code. Java supports
skills, this cheatsheet aims to be your reliable multithreading through
companion, offering quick access to essential the Thread class.
concepts, best practices, and code snippets. Runnable The Runnable interface is
used for defining the
INTRODUCTION
INTRODUCTION code that can be
executed by a thread. It
Java is a powerful and versatile programming provides a way to
language known for its support for concurrent encapsulate the task or
programming. Concurrency allows you to execute job that a thread should
multiple tasks in parallel, making your applications perform.
more efficient and responsive. However,
Synchronization Synchronization
concurrent programming introduces challenges
mechanisms like
such as synchronization, thread safety, and
synchronized blocks and
avoiding common pitfalls like deadlocks and race
methods are used to
conditions.
control access to critical
sections of code,
This Java Concurrency Cheatsheet serves as a quick
preventing multiple
reference guide to essential concepts, classes, and
threads from accessing
techniques for writing concurrent Java
them simultaneously.
applications. Whether you’re a beginner looking to
grasp the basics of multithreading or an Locks and Mutexes Locks (e.g.,
experienced developer aiming to optimize ReentrantLock) are
performance, this cheatsheet provides a explicit mechanisms
comprehensive overview of key topics. used to manage access
to shared resources,
BASICCONCEPTS
BASIC CONCEPTS allowing threads to
acquire and release
Let’s start by providing a foundation for locks for controlled
understanding and working with concurrent access.
programming in Java. Concurrent programming is Race Conditions A race condition occurs
essential for leveraging the power of modern multi- when two or more
core processors and creating responsive and threads access shared
efficient applications that can perform tasks data concurrently, and
concurrently and in parallel. the final result depends
on the order of
execution, leading to
unpredictable behavior.
Proper synchronization
prevents race
conditions.

JAVACODEGEEKS.COM | © EXELIXIS MEDIA P.C. VISIT JAVACODEGEEKS.COM FOR MORE!


2 JAVA CONCURRENCY

Concept Description Concept Description

Data Race A data race is a specific Java Memory Model JMM defines the rules
type of race condition (JMM) and guarantees for how
where two or more threads interact with
threads concurrently memory, ensuring
access shared data, and visibility of changes
at least one of them made by one thread to
modifies the data. Data other threads.
races can result in
undefined behavior and JMM"HAPPENS-BEFORE"
"HAPPENS-BEFORE"
should be avoided.
JMM
RELATIONSHIP
RELATIONSHIP
Deadlocks Deadlocks occur when
two or more threads are The "happens-before" relationship describes the
blocked, waiting for guarantees and constraints JMM applies regarding
resources that will the order of actions and visibility of memory
never be released. changes in a multi-threaded environment. It is
Identifying and avoiding critical for establishing a consistent and predictable
deadlocks is essential in order of operations when we have multiple threads
concurrent accessing the same resources. It helps prevent
programming. issues like data races, ensures that memory changes
Atomic Operations Atomic operations are are visible to other threads when necessary, and
thread-safe operations provides a foundation for reasoning about the
that can be performed behavior of concurrent Java programs.
without interference
A "happens-before" relationship has the following
from other threads. Java
properties:
provides atomic classes
like AtomicInteger and
• Guarantee of Order: The "happens-before"
AtomicReference.
relationship establishes a guarantee that
Thread Local Storage Thread-local storage actions performed before an action "happens-
allows each thread to before" another action, will be seen by other
have its own copy of a threads in the expected order. It ensures that
variable, which is certain operations are observed as occurring
isolated from other sequentially.
threads. It’s useful for
• Program Order: Actions within a single thread,
storing thread-specific
as defined by the program order, are always
data.
considered to have a "happens-before"
Volatile Keyword The volatile keyword relationship. This means that actions within the
ensures that changes to same thread occur in the order specified by the
a variable are visible to program, as expected.
all threads. It’s used for
• Synchronization: Synchronization actions,
variables accessed by
such as acquiring and releasing locks via
multiple threads
synchronized blocks or ReentrantLocks, create
without
"happens-before" relationships. When a thread
synchronization.
releases a lock, all actions performed within
the synchronized block are guaranteed to be
visible to other threads that subsequently
acquire the same lock.

• Thread Start and Termination: When a


thread starts (via Thread.start()) or terminates
(via Thread.join()), there is a "happens-before"

JAVACODEGEEKS.COM | © EXELIXIS MEDIA P.C. VISIT JAVACODEGEEKS.COM FOR MORE!


3 JAVA CONCURRENCY

relationship between the thread’s start or }


termination and actions that occur within that }
thread.

• Volatile Variable Access: Accesses to volatile The Runnable interface is a functional interface that
variables create "happens-before" represents a task or piece of code that can be
relationships. When a thread writes to a executed concurrently by a thread. It provides a
volatile variable, it guarantees that subsequent way to define the code that a thread should run
reads by other threads will see the most recent without the need to explicitly extend the Thread
write. class. Implementing the Runnable interface allows
• Transitivity: "happens-before" relationships for better separation of concerns and promotes
are transitive. If action A "happens-before" reusability of code.
action B, and action B "happens-before" action
C, then action A also "happens-before" action C.
public class MyRunnable implements
THREADSAND
ANDRUNNABLE
RUNNABLE Runnable {
THREADS public void run() {
The Thread class is a fundamental class for creating // Code to be executed by
and managing threads. It allows you to define and the thread
run concurrent tasks or processes within your for (int i = 1; i <= 5; i++)
application. Threads represent lightweight, {
independent paths of execution that can perform System.out.println
tasks concurrently, making it possible to achieve ("Thread: " + Thread.
parallelism in your programs. currentThread().getId() + " Count: "
+ i);
public class MyThread extends Thread }
{ }
public void run() {
// Code to be executed by public static void main(String[]
the thread args) {
for (int i = 1; i <= 5; i++) // Create two Runnable
{ instances
System.out.println MyRunnable runnable1 = new
("Thread: " + Thread. MyRunnable();
currentThread().getId() + " Count: " MyRunnable runnable2 = new
+ i); MyRunnable();
}
} // Create threads and
associate them with Runnable
public static void main(String[] instances
args) { Thread thread1 = new Thread
// Create two threads (runnable1);
MyThread thread1 = new Thread thread2 = new Thread
MyThread(); (runnable2);
MyThread thread2 = new
MyThread(); // Start the threads
thread1.start();
// Start the threads thread2.start();
thread1.start(); }
thread2.start(); }

JAVACODEGEEKS.COM | © EXELIXIS MEDIA P.C. VISIT JAVACODEGEEKS.COM FOR MORE!


4 JAVA CONCURRENCY

Thread states represent the different phases or Thread State Description


conditions that a thread can be in during its
TERMINATED A thread is in the
lifecycle:
TERMINATED state when it
has completed its
Thread State Description
execution or has been
NEW A thread is in the NEW explicitly terminated.
state when it has been Once terminated, a
created but has not yet thread cannot be
started executing. It is restarted or run again.
not yet eligible to run
and has not yet acquired Thread lifecycle methods:
any system resources.

RUNNABLE A thread is in the Method Description


RUNNABLE state when it is start() Initiates the execution of
eligible to run, and the a thread by invoking its
Java Virtual Machine run() method. When
(JVM) has allocated start() is called, the
resources for its thread transitions from
execution. However, it the NEW state to the
may not be currently RUNNABLE state and
executing. begins execution
BLOCKED A thread is in the concurrently. It’s the
BLOCKED state when it is primary method for
waiting to acquire a starting a new thread.
monitor lock to enter a wait() Used to make a thread
synchronized block or voluntarily give up the
method. It is blocked by monitor lock it holds. It
another thread holding should be called from
the lock. within a synchronized
WAITING A thread is in the block or method. The
WAITING state when it is thread enters the
waiting for a specific WAITING state and
condition to be met releases the lock until
before it can proceed. It it’s notified by another
may be waiting thread.
indefinitely until notify() / notifyAll() Used to wake up one / all
notified by another of the threads that are
thread. waiting using the wait()
TIMED_WAITING Similar to the WAITING method on the same
state, a thread in the object. It allows one / all
TIMED_WAITING state is waiting threads to
waiting for a specific transition back to the
condition. However, it RUNNABLE state, giving
has a timeout and will them a chance to
automatically transition proceed.
to RUNNABLE after the
timeout expires.

JAVACODEGEEKS.COM | © EXELIXIS MEDIA P.C. VISIT JAVACODEGEEKS.COM FOR MORE!


5 JAVA CONCURRENCY

Method Description SYNCHRONIZATION


SYNCHRONIZATION
join() Allows one thread to
wait for the completion THE SYNCHRONIZED KEYWORD
of another thread. When
The synchronized keyword is used to create
a thread calls join() on
synchronized blocks of code, which ensure that
another thread, it will
only one Thread can execute them at a time. It
block until the target
provides a way to control access to critical sections
thread finishes its
of your program, preventing multiple threads from
execution.
accessing them simultaneously.
yield() Suggests to the JVM that
the current thread is To enter a synchronized block, one must acquire a
willing to yield its lock on an object’s monitor. An object’s monitor is a
current CPU time to synchronization mechanism that provides locking
allow other threads to functionality on Object instances. After doing so, all
run. It’s a hint, and the code included in the block can be manipulated
actual behavior depends exclusively and atomically. Upon exiting the
on the JVM’s synchronized block the lock is returned to the
implementation. object’s monitor for other threads to acquire. If the
lock cannot be acquired immediately, the executing
sleep() Pauses the execution of
Thread waits until it becomes available.
the current thread for a
specified amount of time
(in milliseconds). It public class
allows you to introduce
SynchronizedBlockExample {
delays in your program,
private int count = 0;
often used for timing
private Object lock = new
purposes.
Object(); // A lock object for
interrupt() Interrupts the execution synchronization
of a thread by setting its
interrupt status. It can
public void performTask() {
be used to request a
synchronized (lock) { //
thread to gracefully
terminate or to handle Synchronized block using the 'lock'
the interruption in a object
custom way. If the for (int i = 0; i <
thread is waiting, 1000; i++) {
sleeping, or blocked an count++;
InterruptedException is }
thrown. In case you }
catch the exception at }
the interrupted thread
}
level, set its interrupt
status manually by
calling The synchronized keyword can be also specified on
Thread.currentThread(). a method level. For non static methods, the lock is
interrupt() and throw acquired from the monitor of the Object instance
the exception in order to that the method is a member of, or for static
be handled at a higher methods, the Class object monitor of the Class with
level. the method.

public class SynchronizedExample {

JAVACODEGEEKS.COM | © EXELIXIS MEDIA P.C. VISIT JAVACODEGEEKS.COM FOR MORE!


6 JAVA CONCURRENCY

private int count = 0;


public class WaitNotifyExample {
// Synchronized instance method private static final Object lock
public synchronized void = new Object();
increment() { private static boolean isOddTurn
count++; = true;
}
public static void main(String[]
// Synchronized static method args) {
public static synchronized void Thread oddThread = new
decrement() { Thread(() -> {
count--; for (int i = 1; i <= 10;
} i += 2) {
synchronized (lock)
} {
while
(!isOddTurn) {
The lock is reentrant, so if the thread already holds try {
the lock, it can successfully acquire it again.
lock
.wait(); // Wait until it's the odd
class Reentrantcy { thread's turn
private int count = 0; } catch
(InterruptedException e) {
public synchronized void doAll() Thread
{ .currentThread().interrupt();
increment(); }
decrement(); }
} System.out
.println("Odd: " + i);
public synchronized void isOddTurn =
increment() { false; // Satisfy the waiting
count++; condition
} lock.notify();
// Notify the even thread
public synchronized void }
decrement() { }
count--; });
}
} Thread evenThread = new
Thread(() -> {
for (int i = 2; i <= 10;
WAIT()/NOTIFY()/NOTIFYALL() i += 2) {
synchronized (lock)
The most common pattern for synchronizing access
{
to functionality/resources using wait(), notify(),
while (
notifyAll() methods is a condition loop. Let’s see
isOddTurn) {
an example that demonstrates the usage of wait()
and notify() to coordinate two threads to print
try {
alternate numbers: lock
.wait(); // Wait until it's the even

JAVACODEGEEKS.COM | © EXELIXIS MEDIA P.C. VISIT JAVACODEGEEKS.COM FOR MORE!


7 JAVA CONCURRENCY

thread's turn all threads. In other words, there JMM applies a


} catch "happens-before" relationship for the events "write
to a volatile variable" and any subsequent "read
(InterruptedException e) {
from the volatile variable". Therefore, any
Thread
subsequent reads of the variable will see the value
.currentThread().interrupt();
that was set by the most recent write.
}
}
System.out public class VolatileExample {
.println("Even: " + i); private static volatile boolean
isOddTurn = flag = false;
true; // Satisfy the waiting
condition public static void main(String[]
lock.notify(); args) {
// Notify the odd thread Thread writerThread = new
} Thread(() -> {
} try {
}); Thread.sleep(1000);
// Simulate some work
oddThread.start(); } catch
evenThread.start(); (InterruptedException e) {
} Thread.
} currentThread().interrupt();
}
flag = true; // Set the
Things to notice:
flag to true
• In order to use wait(), notify(), notifyAll() on System.out.println("Flag
an object, you need to acquire the lock on this set to true by writerThread.");
object first - both our threads synchronize on });
the lock object to acquire its lock.

• Always wait inside a loop that checks the Thread readerThread = new
condition being waited on. This addresses the Thread(() -> {
timing issue if another thread satisfies the while (!flag) {
condition before the wait begins and also // Busy-wait until
protects your code from spurious wake-ups - the flag becomes true
both our threads wait inside a loop governed }
by the isOddTurn flag. System.out.println("Flag
• Always ensure that you satisfy the waiting is true, readerThread can
condition before calling notify() / notifyAll(). proceed.");
Failing to do so will cause a notification but no });
thread will ever be able to escape its wait loop -
both our threads satisfy the isOddTurn flag for writerThread.start();
the other thread to continue.
readerThread.start();
}
THEVOLATILE
THE VOLATILEKEYWORD
KEYWORD
}

When a variable is declared as volatile, it


guarantees that any read or write operation on that THETHREADLOCAL
THE THREADLOCALCLASS
CLASS
variable is directly performed on the main memory,
ensuring atomic updates and visibility of changes to
ThreadLocal is a class that provides thread-local

JAVACODEGEEKS.COM | © EXELIXIS MEDIA P.C. VISIT JAVACODEGEEKS.COM FOR MORE!


8 JAVA CONCURRENCY

variables. A thread-local variable is a variable that thread3.start();


is unique to each thread, meaning that each thread }
accessing a ThreadLocal variable gets its own }
independent copy of that variable. This can be
useful when you have data that needs to be isolated
and maintained separately for each thread, but also IMMUTABLEOBJECTS
IMMUTABLE OBJECTS
reduce contention for shared resources, which
usually leads to performance bottlenecks. It’s An immutable object is an object whose state
commonly used to store values like user sessions, cannot be modified after it is created. Once an
database connections, and thread-specific state immutable object is initialized, its internal state
without explicitly passing them between methods. remains constant throughout its lifetime. This
property makes immutable objects inherently
Here’s a simple example of how to use ThreadLocal thread-safe because they cannot be modified by
to store and retrieve thread-specific data: multiple threads simultaneously, eliminating the
need for synchronization.

public class ThreadLocalExample { Creating an immutable object involves several key


private static ThreadLocal steps:
<Integer> threadLocal = ThreadLocal
.withInitial(() -> 0); • Make the class final: To prevent inheritance
and ensure that the class cannot be subclassed.
public static void main(String[] • Declare all fields as final: Mark all instance
args) { variables as final to make sure they are
// Create and start three initialized only once, typically within the
threads constructor.
Thread thread1 = new • No setter methods: Do not provide setter
Thread(() -> { methods that allow the modification of the
threadLocal.set(1); // object’s state.
Set a thread-specific value
• Safe publication: this reference does not
System.out.println escape during construction.
("Thread 1: " + threadLocal.get());
• No mutable objects: If the class contains
// Get the thread-specific value
references to mutable objects (objects that can
});
change their state), ensure that those
references are not exposed or allow external
Thread thread2 = new modification.
Thread(() -> {
• Make all fields private: Encapsulate the fields
threadLocal.set(2);
by making them private to restrict direct
System.out.println
access.
("Thread 2: " + threadLocal.get());
}); • Return a new object in methods that modify
state: Instead of modifying the existing object,
create a new object with the desired changes
Thread thread3 = new
and return it.
Thread(() -> {
threadLocal.set(3);
System.out.println public final class ImmutablePerson {
("Thread 3: " + threadLocal.get()); private final String name;
}); private final int age;
private final List
thread1.start(); <ImmutablePerson> family;
thread2.start();

JAVACODEGEEKS.COM | © EXELIXIS MEDIA P.C. VISIT JAVACODEGEEKS.COM FOR MORE!


9 JAVA CONCURRENCY

public ImmutablePerson(String resource or a lock. This results in a standstill where


name, int age, List<ImmutablePerson> none of the threads can make progress. Deadlocks
are typically caused by improper synchronization
family) {
or resource allocation against resources that causes
this.name = name;
blocking. Lest see an example of a deadlock
this.age = age;
scenario involving two threads and two locks:
// Defensive copy
List<ImmutablePerson> copy =
new ArrayList<>(family); public class DeadlockExample {
// Making mutable collection private static final Object
unmodifiable lock1 = new Object();
this.family = Collections private static final Object
.unmodifiableList(copy); lock2 = new Object();
// 'this' is not passed to
anywhere during construction public static void main(String[]
} args) {
Thread thread1 = new
public String getName() { Thread(() -> {
return name; synchronized (lock1) {
} System.out.println
("Thread 1: Holding lock 1...");
public int getAge() { try { Thread.sleep
return age; (100); } catch (InterruptedException
} e) {}
System.out.println
// No setter methods, and fields ("Thread 1: Waiting for lock 2...");
are final synchronized (lock2)
{
// Instead of modifying the System.out
object, return a new object with the .println("Thread 1: Acquired lock
desired changes 2.");
public ImmutablePerson withAge }
(int newAge) { }
return new ImmutablePerson });
(this.name, newAge);
} Thread thread2 = new
Thread(() -> {
// No toString, hashCode and synchronized (lock2) {
equals methods for simplicity System.out.println
} ("Thread 2: Holding lock 2...");
try { Thread.sleep
(100); } catch (InterruptedException
DEADLOCK,LIVELOCK
DEADLOCK, LIVELOCKAND
ANDTHREAD
THREAD
e) {}
STARVATION
STARVATION System.out.println
("Thread 2: Waiting for lock 1...");
DEADLOCK synchronized (lock1)
{
Deadlock is a situation where two or more threads
System.out
are unable to proceed with their execution because
.println("Thread 2: Acquired lock
they are each waiting for the other(s) to release a
1.");

JAVACODEGEEKS.COM | © EXELIXIS MEDIA P.C. VISIT JAVACODEGEEKS.COM FOR MORE!


10 JAVA CONCURRENCY

} public class
} DeadlockResolutionExample {
}); private static final Lock lock1
= new ReentrantLock();
thread1.start(); private static final Lock lock2
thread2.start(); = new ReentrantLock();
}
} public static void main(String[]
args) {
Runnable acquireLocks = ()
In this example:
-> {
• thread1 acquires lock1 and then waits for lock2.
lock1.lock();
try {
• thread2 acquires lock2 and then waits for lock1.
System.out.println
(Thread.currentThread().getName() +
Both threads are now waiting for a resource held
by the other, resulting in a deadlock. The program
": Holding lock 1...");
will hang indefinitely. try {
Thread.sleep(
OVERCOMING DEADLOCK
100);
} catch
Deadlocks can be avoided or resolved by various (InterruptedException e) {
techniques: }
System.out.println
• Use a Timeout: Set a timeout for acquiring
(Thread.currentThread().getName() +
locks. If a thread cannot acquire a lock within a
": Waiting for lock 2...");
specified time, it can release any locks it holds
and retry or abort. This functionality can be
easily implemented using ReentrantLock from
// Attempt to
the java.util.concurrent.locks package. acquire lock2 with a timeout of 500
milliseconds
• Lock Ordering: Establish a consistent order for
boolean
acquiring locks across all threads to prevent
acquiredLock2 = lock2.tryLock(500,
circular waiting as seen in the example below.
TimeUnit.MILLISECONDS);
• Resource Allocation Graph: Use algorithms if (acquiredLock2) {
like the resource allocation graph to detect and
try {
recover from deadlocks.
System.out
• Design for Deadlock Avoidance: Design your .println(Thread.currentThread().getN
multi-threaded code to minimize the potential ame() + ": Acquired lock 2.");
for deadlocks, such as using higher-level } finally {
abstractions like the java.util.concurrent
lock2.
classes.
unlock();
}
import } else {
java.util.concurrent.TimeUnit; System.out
import .println(Thread.currentThread().getN
java.util.concurrent.locks.Lock; ame() + ": Timeout while waiting for
import lock 2.");
java.util.concurrent.locks.Reentrant }
Lock; } finally {
lock1.unlock();

JAVACODEGEEKS.COM | © EXELIXIS MEDIA P.C. VISIT JAVACODEGEEKS.COM FOR MORE!


11 JAVA CONCURRENCY

} ExecutorService is a subinterface of Executor that


}; extends the functionality by providing methods for
managing the lifecycle of the executor and
controlling the execution of tasks. In other words,
// Consistent order for
ExecutorService is the core interface for thread
acquiring locks and use of timeouts
pools.
Thread thread1 = new Thread
(acquireLocks); ExecutorService implementation classes offer
Thread thread2 = new Thread various ways to manage and execute tasks
(acquireLocks); concurrently, each with its own advantages and use
cases. You can find the most commonly used in the
thread1.start(); table below. Choose the appropriate
thread2.start(); implementation based on your specific
requirements, but remember, when sizing thread
}
pools, it is often useful to base their size on the
}
number of logical cores the machine running your
code has. You can get that value by calling
Runtime.getRuntime().availableProcessors()
LIVELOCK
Executorservice Description
Livelock is a situation where two or more threads
Implementation
are actively trying to resolve a conflict but end up
causing repeated state changes without making any ThreadPoolExecutor A versatile and
progress. In a livelock, threads are not blocked but customizable executor
are busy responding to each other’s actions, and the service that allows you
system remains in an undesirable state. to create thread pools
with specified core and
maximum thread
THREAD STARVATION
counts, custom thread
Thread starvation occurs when a thread is unable factory, and more.
to make progress because it is constantly waiting ScheduledThreadPoolExec Extends
for a resource or access to a critical section that is utor ThreadPoolExecutor to
always being acquired by other threads. This provide scheduling
results in the affected thread not getting a fair capabilities for
share of CPU time. executing tasks at
specific times or
THEJAVA.UTIL.CONCURRENT
THE JAVA.UTIL.CONCURRENT intervals.
PACKAGE
PACKAGE ForkJoinPool A specialized
ExecutorService
The java.util.concurrent package provides a wide
designed for parallel
range of classes and interfaces that support
execution, particularly
concurrent and multithreaded programming. These
suited for recursive
classes offer high-level abstractions for managing
tasks and algorithms
threads, synchronization, and concurrent data
using the Fork-Join
structures, making it easier to write efficient and
framework.
thread-safe code. Here’s an overview of some of its
most popular classes and interfaces. WorkStealingPool An implementation of
ForkJoinPool that uses a
work-stealing algorithm
EXECUTOR & EXECUTORSERVICE
for efficiently
Executor is an interface that represents an object distributing tasks among
capable of executing tasks asynchronously. It worker threads.
decouples the task submission from task execution.

JAVACODEGEEKS.COM | © EXELIXIS MEDIA P.C. VISIT JAVACODEGEEKS.COM FOR MORE!


12 JAVA CONCURRENCY

Executorservice Description Task Type Description


Implementation
Callable Tasks Callable tasks are
SingleThreadExecutor Creates an executor similar to runnables but
service with a single can return a result or
worker thread, suitable throw an exception.
for sequentially They implement the
executing tasks one at a Callable<V> interface.
time.
Asynchronous Tasks Asynchronous tasks are
FixedThreadPool A fixed-size thread pool often represented by the
executor that manages a Future<V> interface and
predetermined number can run independently
of worker threads, ideal of the calling thread.
for a fixed workload. FutureTask is a concrete
CachedThreadPool implementation of
A thread pool executor
Future that allows you to
that can adaptively
wrap a Callable or
adjust the number of
Runnable and use it with
threads based on task
executors.
demand, suitable for
short-lived and bursty
Tasks are submitted to the executor service using
tasks.
ExecutorService#submit, ExecutorService#invokeAll,
SingleThreadScheduledEx Creates a single-
or ExecutorService#invokeAny.
ecutor threaded scheduled
executor, which allows Most methods of the ExecutorService return
scheduling tasks for Future<V> instances. Future is an interface that
execution at specific represents the result of an asynchronous
times or with fixed-rate computation. It exposes methods to examine if the
intervals. computation is complete or block until the result is
FixedScheduledThreadPoo A fixed-size thread pool available. Below is an example.
l with scheduling
capabilities, combining
import
features of a fixed-size
java.util.concurrent.ExecutorService
thread pool with task
scheduling.
;
import
Additionally, java.util.concurrent provides the java.util.concurrent.Executors;
Executors class which contains static factory
methods for easily creating the aforementioned public class ExecutorServiceExample
thread pool types and more. {

Available task types are shown in the table below. public static void main(String[]
args) {
Task Type Description
// Create an ExecutorService
Runnable Tasks Runnable tasks are using a fixed-size thread pool with
simple, non-returning 2 threads.
tasks that implement the ExecutorService
Runnable interface and executorService = Executors
perform actions without
.newFixedThreadPool(2);
producing a result.

// Define a Runnable task

JAVACODEGEEKS.COM | © EXELIXIS MEDIA P.C. VISIT JAVACODEGEEKS.COM FOR MORE!


13 JAVA CONCURRENCY

Runnable runnableTask = () submit a list of Callable tasks and


-> { wait for the first completed task.
String threadName = String firstResult =
Thread.currentThread().getName(); executorService.invokeAny(callableTa
System.out.println("Task sks);
1 executed by " + threadName); System.out.println
}; ("First completed task: " +
firstResult);
// Define a list of Callable } catch (Exception e) {
tasks e.printStackTrace();
List<Callable<String>> }
callableTasks = List.of(
() -> { // Shutdown the
String threadName = ExecutorService to stop accepting
Thread.currentThread().getName(); new tasks
return "Task 2 executorService.shutdown();
executed by " + threadName; }
}, }
() -> {
String threadName =
Thread.currentThread().getName(); SEMAPHOR
return "Task 3 The Semaphore class is a synchronization primitive
executed by " + threadName; that allows a fixed number of threads to access a
} resource or a section of code concurrently. This is
); especially useful for scenarios where you want to
limit concurrency, manage access to a pool of
// Submit the task to the resources, or protect a critical section of code.
ExecutorService Semaphore is initialized with a count, a set of
executorService.submit permits. Threads may call acquire() to acquire a
permit. Each acquire() blocks if necessary until a
(runnableTask);
permit is available, and then takes it. Threads may
call release() to add a permit, potentially releasing
try {
a blocking acquirer.
// Use invokeAll to
submit a list of Callable tasks and
COUNTDOWNLATCH
wait for all tasks to complete.
List<Future<String>> CountDownLatch is a synchronization construct that
futures = executorService.invokeAll allows one or more threads to wait for a set of
(callableTasks); operations to complete before they proceed.
CountDownLatch is initialized with a count, the
// Print the results of number of operations needed to be completed
the Callable tasks, call to get() before a thread is allowed to continue. Threads may
call await() to wait for the count to reach 0 and
waits until the result is available
then proceed. Threads may call countDown() to
for (Future<String>
reduce the count by one when they complete an
future : futures) {
operation.
System.out.println
(future.get());
CYCLICBARRIER
}
CyclicBarrier is a synchronization barrier that
// Use invokeAny to

JAVACODEGEEKS.COM | © EXELIXIS MEDIA P.C. VISIT JAVACODEGEEKS.COM FOR MORE!


14 JAVA CONCURRENCY

allows a set of threads to wait for each other to Concurrent Collection Description
reach a common point before continuing execution. Class
It’s commonly used to synchronize multiple threads
BlockingQueue Blocking queues are
that perform different subtasks and need to wait
(LinkedBlockingQueue, thread-safe, bounded or
for each other before proceeding. CyclicBarrier is
DelayQueue, unbounded queues that
initialized with a count, the number of threads to
PriorityBlockingQueue, support blocking
wait before continuing, and a function called when
SynchronousQueue) operations for producer-
the count is reached and threads are allowed to
consumer scenarios. In
continue. Threads may call await() to wait for the
DelayQueue elements are
count to reach the designated number before
removed based on their
allowed to proceed operations.
delay, in
PriorityBlockingQueue
CONCURRENT COLLECTIONS based on a Comparator
and in SynchronousQueue
These concurrent collection classes provide thread-
an element is removed
safe data structures for various use cases, allowing
only when a new one
multiple threads to access and modify data
has arrived.
concurrently while ensuring data consistency and
minimizing contention. The choice of which class to ConcurrentLinkedQueue A thread-safe, non-
use depends on the specific needs of your blocking, and
concurrent application. unbounded queue based
on a linked node
Concurrent Collection Description structure, suitable for
Class high-concurrency
producer-consumer
ConcurrentHashMap A highly concurrent,
scenarios.
thread-safe
implementation of the ConcurrentLinkedDeque A thread-safe, non-
Map interface, designed blocking, double-ended
for efficient read and queue that supports
write operations in concurrent access and
multithreaded modifications from both
environments. ends.

ConcurrentSkipListMap A concurrent, sorted CopyOnWriteArrayList A list that creates a new


map that is based on a copy of its internal array
skip list data structure, whenever a
providing concurrent modification is made,
access and sorted order. ensuring thread safety
for read-heavy
workloads.
CopyOnWriteArraySet A thread-safe set that is
backed by a
CopyOnWriteArrayList,
providing thread safety
for read-heavy sets.
ConcurrentSkipListSet A concurrent, sorted set
that is based on a skip
list data structure,
providing concurrent
access and sorted order.

JAVACODEGEEKS.COM | © EXELIXIS MEDIA P.C. VISIT JAVACODEGEEKS.COM FOR MORE!


15 JAVA CONCURRENCY

ATOMICS System.out.println
("Incremented value: " +
The java.util.concurrent.atomic package provides incrementedValue);
classes that support atomic operations on single
variables. These classes are designed to be used in
// Add a specific value
multi-threaded applications to ensure that
atomically
operations on shared variables are performed
atomically without the need for explicit
int addedValue = atomicInt
synchronization. This helps avoid data races and .addAndGet(5);
ensures thread safety. System.out.println("Added
value: " + addedValue);
Common Atomic Classes:
// Compare and set the value
• AtomicInteger: An integer value that can be
atomically
atomically incremented, decremented, or
boolean updated = atomicInt
updated.
.compareAndSet(10, 15);
• AtomicLong: A long value that supports atomic System.out.println("Value
operations. updated? " + updated);
• AtomicBoolean: A boolean value with atomic
operations for setting and getting. // Get the current value
• AtomicReference: A generic reference type that int currentValue =
supports atomic updates. atomicInt.get();
System.out.println("Current
• AtomicStampedReference: A variant of
value: " + currentValue);
AtomicReference that includes a version stamp
to detect changes.
}
}
• AtomicIntegerArray, AtomicLongArray,
AtomicReferenceArray: Arrays of atomic values.

LOCKS
They are suitable for scenarios where you need to
perform operations like increment, compare-and- Locks provide more flexible and advanced locking
set, and update on variables without risking data mechanisms compared to synchronized blocks,
corruption due to concurrent access. Here’s a including features like reentrancy, fairness, and
simple example using AtomicInteger to demonstrate read-write locking. The java.util.concurrent.locks
atomic operations. package contains two interfaces, Lock and
ReadWriteLock and their implementation classes
ReentrantLock and ReentrantReadWriteLock
import
respectively.
java.util.concurrent.atomic.AtomicIn
teger; ReentrantLock is a reentrant mutual exclusion lock
with the same basic behavior as synchronized blocks
public class AtomicExample { but with additional features. It can be used to
public static void main(String[] control access to a shared resource and provides
args) { more flexibility and control over locking such as
AtomicInteger atomicInt = obtaining information about the state of the lock,
non-blocking tryLock(), and interruptible locking.
new AtomicInteger(0);
In this example, we use a ReentrantLock to protect a
critical section of code.
// Increment the atomic
integer atomically
int incrementedValue = import
atomicInt.incrementAndGet(); java.util.concurrent.locks.Reentrant

JAVACODEGEEKS.COM | © EXELIXIS MEDIA P.C. VISIT JAVACODEGEEKS.COM FOR MORE!


16 JAVA CONCURRENCY

Lock;
import
public class ReentrantLockExample { java.util.concurrent.locks.ReadWrite
private static ReentrantLock Lock;
lock = new ReentrantLock(); import
java.util.concurrent.locks.Reentrant
public static void main(String[] ReadWriteLock;
args) {
Runnable task = () -> { public class ReadWriteLockExample {
lock.lock(); // Acquire private static ReadWriteLock
the lock readWriteLock = new
try { ReentrantReadWriteLock();
System.out.println private static String sharedData
("Thread " + Thread.currentThread = "Initial Data";
().getId() + " has acquired the
lock."); public static void main(String[]
// Perform some args) {
critical section operations Runnable reader = () -> {
Thread.sleep(1000); readWriteLock.
} catch readLock().lock(); // Acquire the
(InterruptedException e) { read lock
Thread. try {
currentThread().interrupt(); System.out.println
} finally { ("Reader Thread " + Thread
lock.unlock(); // .currentThread().getId() + " is
Release the lock reading: " + sharedData);
System.out.println // Reading shared
("Thread " + Thread.currentThread data
().getId() + " has released the } finally {
lock."); readWriteLock
} .readLock().unlock(); // Release the
}; read lock
}
// Create multiple threads };
to access the critical section
for (int i = 0; i < 3; i++) Runnable writer = () -> {
{ readWriteLock.
new Thread(task). writeLock().lock(); // Acquire the
start(); write lock
} try {
} sharedData = "New
} Data";
System.out.println
("Writer Thread " + Thread
ReentrantReadWriteLock provides separate locks for .currentThread().getId() + " is
reading and writing. It’s used to allow multiple
writing: " + sharedData);
threads to read a shared resource simultaneously,
// Writing to the
while ensuring that only one thread can write to
shared data
the resource at a time. Here’s an example.
} finally {

JAVACODEGEEKS.COM | © EXELIXIS MEDIA P.C. VISIT JAVACODEGEEKS.COM FOR MORE!


17 JAVA CONCURRENCY

readWriteLock
.writeLock().unlock(); // Release
the write lock
}
};

// Create multiple reader


and writer threads
for (int i = 0; i < 3; i++)
{
new Thread(reader).
start();
}
for (int i = 0; i < 2; i++)
{
new Thread(writer).
start();
}
}
}

JCG delivers over 1 million pages each month to more than 700K software
developers, architects and decision makers. JCG offers something for everyone,
including news, tutorials, cheat sheets, research guides, feature articles, source code
and more.
CHEATSHEET FEEDBACK
WELCOME
support@javacodegeeks.com

Copyright © 2014 Exelixis Media P.C. All rights reserved. No part of this publication may be SPONSORSHIP
reproduced, stored in a retrieval system, or transmitted, in any form or by means electronic, OPPORTUNITIES
mechanical, photocopying, or otherwise, without prior written permission of the publisher. sales@javacodegeeks.com

JAVACODEGEEKS.COM | © EXELIXIS MEDIA P.C. VISIT JAVACODEGEEKS.COM FOR MORE!

You might also like