Sign in Get started
T OP S T ORY S UBMIT
You have 2 free stories left this month. Sign up and get an extra one for free.
Java Concurrency: The Basics
The power of multi-threading using Java
Tiago Albuquerque Follow
Oct 23, 2019 · 8 min read
This article describes the basics of Java Concurrency API, and how to
assign work to be done asynchronously.
Why not do the work concurrently?
A thread is the smallest unit of execution that can be scheduled by the
operating system, while a process is a group of associated threads that
execute in the same, shared environment.
We call “system thread” the thread created by the JVM that runs in the
background of the application (e.g. garbage-collector), and a “user-
defined thread” those which are created by the developer.
First, we need to know that in a standard Java program, all the work we
implement is done in the main thread, so the work is done sequentially.
To change that behavior, we can create our own threads and program
them to do the work “at the same time”, creating a multi-threaded
environment.
We can create threads extending the Thread class or implement the
Runnable interface. Both use the method run() to execute asynchronous
work. A thread must be started by its start() method, otherwise the work
is not forked in a different thread.
. . .
Extending Thread class
By extending the Thread class, we shall override the “run()” method.
Given the two classes “Ping” and “Pong” below, they simulates 3 time
processing job that takes from 0 to 4 seconds to accomplish each one:
class Ping extends Thread {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
// simulates work that takes between from 0 to 4 sec
try { Thread.sleep(new Random().nextInt(4000)); }
catch (InterruptedException e) { }
System.out.println("ping");
}
}
}
class Pong extends Thread {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
// simulates work that takes between from 0 to 4 sec
try { Thread.sleep(new Random().nextInt(4000)); }
catch (InterruptedException e) { }
System.out.println("pong");
}
}
}
If we instantiate them and call each method run(), the work will be done
sequentially in the main thread, which is not our intention here:
public class MainClass {
public static void main(String[] args) {
System.out.println("Start of main thread");
Thread ping = new Ping();
Thread pong = new Pong();
ping.run(); // wrong
pong.run(); // wrong
System.out.println("End of main thread");
}
}
Output is always:
Start of main thread
ping
ping
ping
pong
pong
pong
End of main thread
But if we start the threads calling its start method, the work will be done
asynchronously and there is no guarantee of the execution order.
public class MainClass {
public static void main(String[] args) {
System.out.println("Start of main thread");
Thread ping = new Ping();
Thread pong = new Pong();
ping.start(); // async
pong.start(); // async
System.out.println("End of main thread");
}
}
Output may be:
Start of main thread
End of main thread
pong
ping
ping
pong
pong
ping
. . .
Implementing Runnable interface
Another way to create a thread is to implement the Runnable interface,
which can be an advantage if your worker class already extends another
class. Since there is no multiple-inheritance in Java, it would be
impossible to extend to the Thread class.
The only modifications in the previous code is the implements keyword in
worker classes:
class Ping implements Runnable {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
// simulates work that takes between from 0 to 4 sec
try { Thread.sleep(new Random().nextInt(4000)); }
catch (InterruptedException e) { }
System.out.println("ping");
}
}
}
class Pong implements Runnable {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
// simulates work that takes between from 0 to 4 sec
try { Thread.sleep(new Random().nextInt(4000)); }
catch (InterruptedException e) { }
System.out.println("pong");
}
}
}
And the instantiation of the thread is passing by the Runnable
implementation in its constructor.
public class MainClass {
public static void main(String[] args) {
System.out.println("Start of main thread");
Thread ping = new Thread(new Ping());
Thread pong = new Thread(new Pong());
ping.start(); // async
pong.start(); // async
System.out.println("End of main thread");
}
}
Again there is no guarantee of the execution order.
Since the Runnable interface is a functional interface, we can use a
lambda expression to create the Runnable instance. The readability of
the code gets a little messy (in my opinion), but still a way to go.
public class MainClass {
public static void main(String[] args) {
System.out.println("Start of main thread");
Thread ping = new Thread(() -> {
for (int i = 0; i < 3; i++) {
try { Thread.sleep(new Random().nextInt(2000)); }
catch (InterruptedException e) { }
System.out.println("ping");
}
});
Thread pong = new Thread(() -> {
for (int i = 0; i < 3; i++) {
try { Thread.sleep(new Random().nextInt(2000)); }
catch (InterruptedException e) { }
System.out.println("pong");
}
});
ping.start(); // async
pong.start(); // async
System.out.println("End of main thread");
}
}
. . .
Threads attributes and methods
To ensure that the main thread finish its execution after the worker
threads, we can join it, pausing its execution while the worker threads
are alive.
public class MainClass {
public static void main(String[] args) throws InterruptedException
{
System.out.println("Start of main thread");
Thread ping = new Thread(() -> {
for (int i = 0; i < 3; i++) {
try { Thread.sleep(new Random().nextInt(2000)); }
catch (InterruptedException e) { }
System.out.println("ping");
}
});
Thread pong = new Thread(() -> {
for (int i = 0; i < 3; i++) {
try { Thread.sleep(new Random().nextInt(2000)); }
catch (InterruptedException e) { }
System.out.println("pong");
}
});
ping.start();
pong.start();
ping.join();
pong.join();
// will be printed last:
System.out.println("End of main thread");
}
}
The output may be as follows, and its guaranteed that the last line will be
the “End of main thread” in the example above.
Start of main thread
pong
pong
ping
pong
ping
ping
End of main thread
Threads has an attribute name, that we can use to identify them during
program execution. The default name is “Thread-n”, where ’n’ is the
incremental number of creation.
// setting thread name by constructor or setter
new Thread(runnableInstante,"My Thread Name");
threadInstance.setName("Ping Thread");
// getting thread name
threadInstance.getName();
Thread priority is a numeric (integer) value associated with a thread that
is taken into consideration by the scheduler when determining which
thread
should currently be executing. There are static constants that helps us
out:
Thread.MIN_PRIORITY // int value = 1
Thread.NORM_PRIORITY // int value = 5, it's the default value
Thread.MAX_PRIORITY // int value =10
An issue to be aware of is the synchronization between data accessed by
threads. If an value is accessed by many threads to make computation, its
results can varies depending on the concurrent read made.
For example, let's assume a simple counter class:
class Counter {
int count;
public void increment() {
count++; // count = count + 1
}
}
And the main class that consumes it using two async threads:
public class MainClass {
public static void main(String[] args) throws InterruptedException
{
Counter counter = new Counter();
Thread c1 = new Thread(() -> {
for (int i = 0; i < 500; i++) {
counter.increment();
}
});
Thread c2 = new Thread(() -> {
for (int i = 0; i < 500; i++) {
counter.increment();
}
});
c1.start(); // async counter thread 1 start
c2.start(); // async counter thread 1 start
// awating async threads to finish
c1.join();
c2.join();
System.out.println(counter.count);
}
}
The output varies on a number equals or less than 1000. The issue is on
the ‘cont’ attribute, that can had been incremented by the other thread
while it's being read by the first one. To ensure that just one thread will
be executing the ‘increment()’ method, we can use the synchronized
keyword, so we always get the correct sum of values (‘1000’, in this case).
class Counter {
int count;
public synchronized void increment() {
count++; // count = count + 1
}
}
. . .
Interface ExecutorService and Threads / Callables
The ExecutorService interface is the easiest and recommended way to
create and manage async tasks. We must first obtain an instance of an
ExecutorService by using some factory methods, and then send the service
tasks to be processed.
To instantiate an ExecutorService, we should use one of the Executors
static methods, depending of the way we want to schedule the tasks and
the amount of threads used to do the work.
Executors.newSingleThreadExecutor() : Creates a single-threaded
executor. Results are processed sequentially.
Executors.newFixedThreadPool(int nrOfThreads) : Creates a thread
pool that reuses a fixed number of threads.
Executors.newCachedThreadPool() : Creates a thread pool that creates
new
threads as needed, but will reuse previous threads when they are
available.
Executors.newSingleThreadScheduledExecutor() : Creates a single-
threaded executor that can schedule commands to run after a given
delay or to execute periodically.
Executors.newScheduledThreadPool(int nrOfThreads) : Creates a
thread pool that can schedule commands to run after a given delay or
to execute periodically.
First we create a Runnable implementation:
class TaskRunn implements Runnable {
@Override
public void run() {
System.out.println("I am " +
Thread.currentThread().getName());
}
}
And then assign the work to an ExecutorService to manage:
public class MainClass {
public static void main(String[] args) {
// pool of 4 threads
ExecutorService executorService =
Executors.newFixedThreadPool(4);
// creates 4 runnables to be executed
for (int i = 0; i < 4; i++) {
TaskRunn runnable = new TaskRunn();
executorService.execute(runnable);
}
executorService.shutdown(); // shuts down the executor service
}
The output may be (no order guaranteed):
I am pool-1-thread-2
I am pool-1-thread-4
I am pool-1-thread-1
I am pool-1-thread-3
When we need to get a returned value, we should implement a Callable
instead of a Runnable. The Callable interface has the “call()” method that
returns a value and can throw a checked exception.
class TaskCall implements Callable<String> {
@Override
public String call() throws Exception {
return "I am " + Thread.currentThread().getName();
}
}
When invoking a Callable task within ExecutorService, we get a Future
object, which represents the result of async computation when it is over:
public class MainClass {
public static void main(String[] args) {
// pool of 4 threads
ExecutorService executorService =
Executors.newFixedThreadPool(4);
List<Future<String>> futures = new ArrayList<>();
// creates 4 callables to be executed
for (int i = 0; i < 4; i++) {
TaskCall callable = new TaskCall();
Future<String> future = executorService.submit(callable);
futures.add(future);
}
// getting tasks results
futures.forEach(f -> {
try { System.out.println(f.get()); }
catch (Exception e) { }
});
executorService.shutdown(); // shuts down the executor service
}
We can user the “invokeAll()” method to send a list of callables to be
executed asynchronoulsy:
public class BasicConcurrencyTests {
public static void main(String[] args)
throws InterruptedException {
// pool of 4 threads
ExecutorService executorService =
Executors.newFixedThreadPool(4);
// creates 3 callables to be executed
List<Callable<String>> callables = Arrays.asList(
new TaskCall(), new TaskCall(), new TaskCall());
List<Future<String>> futures =
executorService.invokeAll(callables);
// getting tasks results
futures.forEach(f -> {
try { System.out.println(f.get()); }
catch (Exception e) { }
});
executorService.shutdown(); // shuts down the executor service
}
Example of output:
I am pool-1-thread-1
I am pool-1-thread-2
I am pool-1-thread-3
There is a lot more, like thread safe data structures and scheduled tasks,
but I guess this content covers the very basics of Java concurrent API.
Java T hreads Concurrency
88 claps
WRIT T EN BY
Tiago Albuquerque Follow
Software Craftsman
The Startup Follow
Medium's largest active publication, followed by +656K
people. Follow to join our community.
Write the rst response
More From Medium
FizzBuzz in MySQL Tests Are for the Future Tutorial: A Simple Simple CSS
Using Stored Tyler Hawkins in Better Framework For methodology
Procedures Programming Optimization Andy Dłubak
Jude Dutton Programming In Python
Using PuLP, Gurobi, and
CPLEX
Ehsan Khodabandeh
Decode JSON in Swift Concrete Example of [Tensor ow 101] What Using Bash for
with intermediate types Web Scraping with does it mean to reduce templating
Jeroen de Vrind in Short Swift Financial Data axis? con guration les in
Stories Alex-Adrien Auger in Sipios Aerin Kim Docker containers
Ivan Tuzhilkin in T he Startup
Discover Medium Make Medium yours Become a member
Welcome to a place where words matter. On Medium, smart Follow all the topics you care about, and we’ll deliver the Get unlimited access to the best stories on Medium — and
voices and original ideas take center stage - with no ads in best stories for you to your homepage and inbox. Explore support writers while you’re at it. Just $5/month. Upgrade
sight. Watch
About Help Legal