window.pipedriveLeadboosterConfig = { base: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version: 2, } ;(function () { var w = window if (w.LeadBooster) { console.warn('LeadBooster already exists') } else { w.LeadBooster = { q: [], on: function (n, h) { this.q.push({ t: 'o', n: n, h: h }) }, trigger: function (n) { this.q.push({ t: 't', n: n }) }, } } })() Concurrency in Java Part 1 - Introduction - The Codest
The Codest
  • About us
  • Services
    • Software Development
      • Frontend Development
      • Backend Development
    • Staff Augmentation
      • Frontend Developers
      • Backend Developers
      • Data Engineers
      • Cloud Engineers
      • QA Engineers
      • Other
    • It Advisory
      • Audit & Consulting
  • Industries
    • Fintech & Banking
    • E-commerce
    • Adtech
    • Healthtech
    • Manufacturing
    • Logistics
    • Automotive
    • IOT
  • Value for
    • CEO
    • CTO
    • Delivery Manager
  • Our team
  • Case Studies
  • Know How
    • Blog
    • Meetups
    • Webinars
    • Resources
Careers Get in touch
  • About us
  • Services
    • Software Development
      • Frontend Development
      • Backend Development
    • Staff Augmentation
      • Frontend Developers
      • Backend Developers
      • Data Engineers
      • Cloud Engineers
      • QA Engineers
      • Other
    • It Advisory
      • Audit & Consulting
  • Value for
    • CEO
    • CTO
    • Delivery Manager
  • Our team
  • Case Studies
  • Know How
    • Blog
    • Meetups
    • Webinars
    • Resources
Careers Get in touch
Back arrow GO BACK
2022-06-15
Software Development

Concurrency in Java Part 1 – Introduction

The Codest

Rafal Sawicki

Java Developer

Read the first part of our blog series devoted to concurrency in Java. In the following article we will take a closer look at differences between thread and processes, thread pools, executors and many more!

In general, the conventional programming approach is sequential. Everything in a program happens one step at a time.
But, in fact, the parallel is how the whole world runs – it is the ability to execute more than one task simultaneously.

Thread vs. process

To discuss such advanced topics as concurrency in Java or multithreading, we have to settle on some common definitions to be sure we are on the same page.

Let’s start with the basics. In the non-sequential world, we have two kinds of concurrency representants: processes and
threads. A process is an instance of the program running. Normally, it is isolated from other processes.
The operating system is responsible for assigning resources to each process. Moreover, it acts as a conductor that
schedules and controls them.

Thread is a kind of a process but on a lower level, therefore it is also known as light thread. Multiple threads can run in one
process. Here the program acts as a scheduler and a controller for threads. This way individual programs appear to do
multiple tasks at the same time.

The fundamental difference between threads and processes is the isolation level. The process has its own set of
resources, whereas the thread shares data with other threads. It may seem like an error-prone approach and indeed it is. For
now, let’s not focus on that as it’s beyond the scope of this article.

Processes, threads – ok… But what exactly is concurrency? Concurrency means you can execute multiple tasks at the same
time. It does not mean those tasks have to run simultaneously – that’s what parallelism is. Concurrenc in Javay also does not
require you to have multiple CPUs or even multiple cores. It can be achieved in a single-core environment by leveraging
context switching.

A term related to concurrency is multithreading. This is a feature of programs that allows them to execute several tasks at once. Not every program uses this approach but the ones that do can be called multithreaded.

We are almost ready to go, just one more definition. Asynchrony means that a program performs non-blocking operations.
It initiates a task and then goes on with other things while waiting for the response. When it gets the response, it can react to it.

All that jazz

By default, each Java application runs in one process. In that process, there is one thread related to the main() method of
an application. However, as mentioned, it is possible to leverage the mechanisms of multiple threads within one
program.

Runnable

Thread is a Java class in which the magic happens. This is the object representation of the before mentioned thread. To
create your own thread, you can extend the Thread class. However, it is not a recommended approach. Threads should be used as a mechanism that run the task. Tasks are pieces of code that we want to run in a concurrent mode. We can define them using the Runnable interface.

But enough theory, let’s put our code where our mouth is.

Problem

Assume we have a couple of arrays of numbers. For each array, we want to know the sum of the numbers in an array. Let’s
pretend there are a lot of such arrays and each of them is relatively big. In such conditions, we want to make use of concurrency and sum each array as a separate task.

int[] a1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int[] a2 = {10, 10, 10, 10, 10, 10, 10, 10};
int[] a3 = {3, 4, 3, 4, 3, 4, 2, 1, 3, 7};

Runnable task1 = () -> {
    int sum = Arrays.stream(a1).sum();
    System.out.println("1. The sum is: " + sum);
};

Runnable task2 = () -> {
    int sum = Arrays.stream(a2).sum();
    System.out.println("2. The sum is: " + sum);
};

Runnable task3 = () -> {
    int sum = Arrays.stream(a3).sum();
    System.out.println("3. The sum is: " + sum);
};

new Thread(task1).start();
new Thread(task2).start();
new Thread(task3).start();

As you can see from the code above Runnable is a functional interface. It contains a single abstract method run()
with no arguments. The Runnable interface should be implemented by any class whose instances are intended to be
executed by a thread.

Once you have defined a task you can create a thread to run it. This can be achieved via new Thread() constructor that
takes Runnable as its argument.

The final step is to start() a newly created thread. In the API there are also run() methods in Runnable and in
Thread. However, that is not a way to leverage concurrency in Java. A direct call to each of these methods results in
executing the task in the same thread the main() method runs.

Thread pools and Executors

When there are a lot of tasks, creating a separate thread for each one is not a good idea. Creating a Thread is a
heavyweight operation and it’s far better to reuse existing threads than to create new ones.

When a program creates many short-lived threads it is better to use a thread pool. The thread pool contains a number of
ready-to-run but currently not active threads. Giving a Runnable to the pool causes one of the threads calls the
run() method of given Runnable. After completing a task the thread still exists and is in an idle state.

Ok, you get it – prefer thread pool instead of manual creation. But how can you make use of thread pools? The Executors
class has a number of static factory methods for constructing thread pools. For example newCachedThredPool() creates
a pool in which new threads are created as needed and idle threads are kept for 60 seconds. In contrast,
newFixedThreadPool() contains a fixed set of threads, in which idle threads are kept indefinitely.

Let’s see how it could work in our example. Now we do not need to create threads manually. Instead, we have to create
ExecutorService which provides a pool of threads. Then we can assign tasks to it. The last step is to close the thread
pool to avoid memory leaks. The rest of the previous code stays the same.

ExecutorService executor = Executors.newCachedThreadPool();

executor.submit(task1);
executor.submit(task2);
executor.submit(task3);

executor.shutdown();

Callable

Runnable seems like a nifty way of creating concurrent tasks but it has one major shortcoming. It cannot return any
value. What is more, we cannot determine if a task is finished or not. We also do not know if it has been completed
normally or exceptionally. The solution to those ills is Callable.

Callable is similar to Runnable in a way it also wraps asynchronous tasks. The main difference is it is able to
return a value. The return value can be of any (non-primitive) type as the Callable interface is a parameterized type.
Callable is a functional interface that has call() method which can throw an Exception.

Now let’s see how we can leverage Callable in our array problem.

int[] a1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int[] a2 = {10, 10, 10, 10, 10, 10, 10, 10};
int[] a3 = {3, 4, 3, 4, 3, 4, 2, 1, 3, 7};

Callable<Integer> task1 = () -> Arrays.stream(a1).sum();
Callable<Integer> task2 = () -> Arrays.stream(a2).sum();
Callable<Integer> task3 = () -> Arrays.stream(a3).sum();

ExecutorService executor = Executors.newCachedThreadPool();
Future<Integer> future1 = executor.submit(task1);
Future<Integer> future2 = executor.submit(task2);
Future<Integer> future3 = executor.submit(task3);

System.out.println("1. The sum is: " + future1.get());
System.out.println("2. The sum is: " + future2.get());
System.out.println("3. The sum is: " + future3.get());

executor.shutdown();

Okay, we can see how Callable is created and then submitted to ExecutorService. But what the heck is Future?
Future acts as a bridge between threads. The sum of each array is produced in a separate thread and we need a way to
get those results back to main().

To retrieve the result from Future we need to call get() method. Here can one of two things happen. First, the
result of the computation performed by Callable is available. Then we get it immediately. Second, the result is not
ready yet. In that case get() method will block until the result is available.

ComputableFuture

The issue with Future is that it works in the ‘push paradigm’. When using Future you have to be like a boss who
constantly asks: ‘Is your task done? Is it ready?’ until it provides a result. Acting under constant pressure is
expensive. Way better approach would be to order Future what to do when it is ready with its task. Unfortunately,
Future cannot do that but ComputableFuture can.

ComputableFuture works in ‘pull paradigm’. We can tell it what to do with the result when it completed its tasks. It
is an example of an asynchronous approach.

ComputableFuture works perfectly with Runnable but not with Callable. Instead, it is possible to supply a task to
ComputableFuture in form of Supplier.

Let’s see how the above relates to our problem.

int[] a1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int[] a2 = {10, 10, 10, 10, 10, 10, 10, 10};
int[] a3 = {3, 4, 3, 4, 3, 4, 2, 1, 3, 7};

CompletableFuture.supplyAsync(() -> Arrays.stream(a1).sum())
                .thenAccept(System.out::println);

CompletableFuture.supplyAsync(() -> Arrays.stream(a2).sum())
                .thenAccept(System.out::println);

CompletableFuture.supplyAsync(() -> Arrays.stream(a3).sum())
                .thenAccept(System.out::println);

The first thing that strikes you is how much shorter this solution is. Besides that, it also looks neat and tidy.

Task to CompletableFuture can be provided by supplyAsync() method that takes Supplier or by runAsync() that
takes Runnable. A callback – a piece of code that should be run on task completion – is defined by thenAccept()
method.

Conclusions

Java provides a lot of different approaches to concurrency. In this article, we barely touched on the topic.

Nevertheless, we covered the basics of Thread, Runnable, Callable, and CallableFuture which poses a good point
for farther topic investigation.

Related articles

Software Development

9 Mistakes to Avoid While Programming in Java

What mistakes should be avoided while programming in Java? In the following piece we answers this question.

The Codest
Rafal Sawicki Java Developer
Enterprise & Scaleups Solutions

The Right Way to Find Top Java Developers

Finding the perfect Java developer can be a daunting task. As the market demand for such professionals grows at an astonishing pace, available sources for talent search can sometimes seem...

The Codest
Grzegorz Rozmus Java Unit Leader
Enterprise & Scaleups Solutions

10 Dubai companies worth watching in 2020

Dubai is the heart of the United Arab Emirates with its increasingly prosperous market of global companies and promising startups. Many can boast about their international success and noteworthy products....

Tuna Pinar
Enterprise & Scaleups Solutions

How Java Can Support Your Business?

Before we start, I would like to remind you about one important thing. Java is not only a programming language.

Bartlomiej Kuczynski
Enterprise & Scaleups Solutions

A Day in a Life of a Programmer at The Codest

You might suspect that the work schedules of programmers do not differ from each other. But this is actually not true! Each startup, software house, even corporation has its own...

The Codest
Pawel Rybczynski Software Engineer

Subscribe to our knowledge base and stay up to date on the expertise from the IT sector.

    About us

    The Codest – International software development company with tech hubs in Poland.

    United Kingdom - Headquarters

    • Office 303B, 182-184 High Street North E6 2JA
      London, England

    Poland - Local Tech Hubs

    • Fabryczna Office Park, Aleja
      Pokoju 18, 31-564 Kraków
    • Brain Embassy, Konstruktorska
      11, 02-673 Warsaw, Poland

      The Codest

    • Home
    • About us
    • Services
    • Case Studies
    • Know How
    • Careers
    • Dictionary

      Services

    • It Advisory
    • Software Development
    • Backend Development
    • Frontend Development
    • Staff Augmentation
    • Backend Developers
    • Cloud Engineers
    • Data Engineers
    • Other
    • QA Engineers

      Resources

    • Facts and Myths about Cooperating with External Software Development Partner
    • From the USA to Europe: Why do American startups decide to relocate to Europe
    • Tech Offshore Development Hubs Comparison: Tech Offshore Europe (Poland), ASEAN (Philippines), Eurasia (Turkey)
    • What are the top CTOs and CIOs Challenges?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Website terms of use

    Copyright © 2025 by The Codest. All rights reserved.

    en_USEnglish
    de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish arArabic it_ITItalian jaJapanese ko_KRKorean es_ESSpanish nl_NLDutch etEstonian elGreek en_USEnglish