Java Multithreading Tutorial With Example

Race Condition – Synchronized Java Example

Synchronization in Java
Synchronization in Java

In this article, we discuss what is the race condition and critical section in Java, and how to prevent race condition using synchronized keyword using a few examples.

You will learn:
– What are Race Conditions and Critical Section?
– How to prevent Race Conditions in Java?
– What is synchronization in Java and how to use synchronized keyword to prevent race condition?.

Similar Topics on Concurrency
What is Thread and How to Create a Thread
CompletableFuture with Example
ConcurrentHashMap with Example

Introduction to Critical Section and Race Condition?

To explain the critical section concept, let’s take a look at the above CriticalSectionDemo class.

In the main method of the CriticalSectionDemo class, we have created two threads using lambdas expression.

In the run() method of those threads we are calling incrementValue() method using the reference of the CriticalSectionDemo class.

Meanwhile, could you observe the problemetic code block in the above example?

The problem is here.

The method incrementValue() is incrementing the instance variable i.

However, incrementing the value i is not an atomic operation.

Firstly, it will read the value of i and then it will increment the value of i.

Consider a scenario when one thread is reading the value of i and at the same time, other threads increment its value.

All things considered, we can say that this.i++ is not thread-safe.

In this case this code block this.i++ called a critical section.

The critical section of the code is a part of the code where the sequence of execution by different threads can alter the expected behavior of the program.

And the condition when multiple threads executing the critical section of code, then race condition occurs.

How to prevent Race condition

In order to prevent the critical section, we have to make sure that the critical section code must execute as an atomic instruction.

There are two popular ways in Java to prevent the race condition.

Synchronization and ThreadLocal.

As we have already covered ThreadLocal in multithreading in java series.

In this tutorial, we will focus on Synchronization using the synchronized keyword.

Synchronized keyword in Java

Synchronized is a modifier in Java used to prevent race conditions.

This keyword is only applicable to the method and block level, we can’t use synchronized for classes and variables.

Now, let us change the above code and add synchronized to prevent the race condition.

Now, if you run the program you won’t face any data-consistency issue and irrespective of thread execution sequence the output will be always as expected.

In the above incrementValue() method, only this.i++ is a critical section, but we have locked the entire method which is not good in the real world scenario.

Therefore, let’s add a synchronized block in the above method.

How Synchronization works

Till now we have discussed how to use synchronized keyword to avoid the race conditions.

But how synchronization works?

Let’s take a look at synchronization internal.

Every object in Java has a unique lock.

Synchronization uses this lock concept internally to avoid the race condition.

Object Level Lock

In the above example when a thread enters into the synchronized block then firstly it acquires the object (this) lock.

Once the thread completes the execution then it releases the lock.

However, while a thread executing the synchronized method/block and other thread also want to execute the same code block then all those threads have to wait until the first thread releases the lock.

Class Level Lock

In java, every class has a unique lock.

Class level locks are applicable for static synchronized methods and blocks.

Therefore if a thread acquires the class level lock then all other threads want to acquire the lock of that class have to wait until the first thread releases the class level lock.

However, you should note that class level lock and object-level locks are completely different.

Conclusion

In this tutorial, we have discussed the critical sections and race conditions.

We have also discussed how to prevent race conditions using synchronization.