is increment operator atomic

Is i++ atomic in Java?

Breaking down into modules in Angularquiz

We usually do not think about concurrency when building simple web applications. That is a mistake. Web applications may process multiple requests at the same time, and inconsistencies coming from concurrency may suddenly cause problems. Today's question is is i++ atomic in Java?. Can we assume that one of the simplest operations is thread-safe?

 

 

Why to care about the increment operator atomicity in Java?

We often do not think much about concurrency even when we develop web applications. That can seem strange because application servers process multiple requests at the same time by its nature. Even so, we still feel safe because in most cases requests are processed in isolation.

Let's think about a simple shop. Two users may buy online the same product. Their requests are processed separately by the web application, in major part: adding the product to a cart, processing payment, creating a new transaction in the registry. However, there is at least one element where processing two requests may meet together - it is updating the warehouse. I mean decreasing the number of available product pieces in the system.

If there were 10 pieces of product available before the shopping and two of them were bought by two customers, the system should show 8 pieces available at the end of the whole processing. It means that probably a single number somewhere might be updated twice by two concurrent threads.

We have got used to such kind of cases thanks to relational databases ACID properties. We usually do not handle that concurrency in Java because both threads meet on requests to the database. And finally, it is the database that is responsible for correctly handling the situation. Taking care of it in a thread-safe manner.

concurrenty at database

A problem arises when there is a shared resource in the web application that does not support ACID. A good example is a simple variable in Java. It is not aware of transactions and usually knows nothing about atomicity. That is why we should be very careful using shared variables like global fields in Spring controllers and services. Unless otherwise configured, services are singletons that may handle multiple requests at the same time by multiple threads that may try to modify the same global field. That may cause concurrency issues.

concurrenty at service

 

Testing if i++ is atomic in Java

Even though we know that using non-ACID shared resources is risky, sometimes it is the best option. But is it really a bad think to have a simple basic integer variable incremented by multiple threads at the same time? The ++ operator seems very basic and fundamental in Java.

I decided to play a little with the code to check it myself. I created a simple unit test that creates a list of 10 tasks that are executed in parallel. Each task increments 100 times the same variable named globalVariable. Those tasks are executed by 10 independent threads. Once they all finish, globalVariable is expected to be equal to 10*100 = 1000.

import org.junit.jupiter.api.Test;

import java.util.List;
import java.util.concurrent.*;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class IncrementingTest {

private Integer globalVariable = 0;

@Test
void shouldExecuteInParallel() throws InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(10);
List<Callable<Void>> subTasks = List.of(
this::increment100times,
this::increment100times,
this::increment100times,
this::increment100times,
this::increment100times,
this::increment100times,
this::increment100times,
this::increment100times,
this::increment100times,
this::increment100times
);
executorService.invokeAll(subTasks);

assertEquals(1000, globalVariable);
}

private Void increment100times() {
for (int iter = 0; iter < 100; iter++) {
globalVariable++;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}

}

You can also notice Thread.sleep(10) between increments. I added it to slow the execution a little bit to increase the chance of collisions.

More information about using ExecutorService is in the article of how to use Executor Service to run tasks by multiple threads.

Then I run the test; it throws an assertion error:

org.opentest4j.AssertionFailedError: 
Expected :1000
Actual :797

Although globalVariable++ was executed 1000 times, the final value of globalVariable was 797. It proves that the ++ increment operator in Java is not atomic. In fact, it consists of a few smaller operations without locking so the whole action is not thread-safe.

If you like what I do, consider buying me a coffee :)

Buy me a coffeeBuy me a coffee