DEV Community

Aswin Arya
Aswin Arya

Posted on

What is Race Condition in Java and How to Prevent It?

In multithreaded programming, multiple threads often access shared resources such as variables, objects, or data structures. If multiple threads try to modify the same data simultaneously without proper synchronization, it can lead to unpredictable results. This situation is known as a Race Condition.

Race conditions are common issues in concurrent programming and can cause data inconsistency, incorrect results, and application instability.


What is a Race Condition?

A Race Condition occurs when two or more threads access shared data at the same time and the final result depends on the order of execution of those threads.

Because thread scheduling is unpredictable, the program may produce different outputs each time it runs.


Example of Race Condition

class Counter {

    int count = 0;

    public void increment() {
        count++;
    }
}

public class RaceConditionExample {

    public static void main(String[] args) throws InterruptedException {

        Counter counter = new Counter();

        Thread t1 = new Thread(() -> {
            for(int i = 0; i < 1000; i++){
                counter.increment();
            }
        });

        Thread t2 = new Thread(() -> {
            for(int i = 0; i < 1000; i++){
                counter.increment();
            }
        });

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println("Final Count: " + counter.count);
    }
}
Enter fullscreen mode Exit fullscreen mode

Expected Output

Final Count: 2000
Enter fullscreen mode Exit fullscreen mode

Actual Output (Possible)

Final Count: 1789
Enter fullscreen mode Exit fullscreen mode

This happens because both threads try to update the same variable simultaneously, leading to lost updates.


Why Race Condition Happens

The operation count++ is not atomic. It actually performs three steps:

  1. Read the current value of count
  2. Increment the value
  3. Write the updated value back

If two threads perform these steps at the same time, the updates may overwrite each other.


How to Prevent Race Conditions

There are several ways to prevent race conditions in Java.


1. Using synchronized Keyword

The synchronized keyword ensures that only one thread can execute the method or block at a time.

Example:

class Counter {

    int count = 0;

    public synchronized void increment() {
        count++;
    }
}
Enter fullscreen mode Exit fullscreen mode

This ensures thread-safe access to the shared variable.


2. Using ReentrantLock

Java provides ReentrantLock for more flexible locking mechanisms.

import java.util.concurrent.locks.ReentrantLock;

class Counter {

    int count = 0;
    ReentrantLock lock = new ReentrantLock();

    public void increment() {

        lock.lock();

        try {
            count++;
        }
        finally {
            lock.unlock();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

3. Using Atomic Variables

Java provides atomic classes that perform thread-safe operations without explicit synchronization.

Example:

import java.util.concurrent.atomic.AtomicInteger;

AtomicInteger count = new AtomicInteger(0);

count.incrementAndGet();
Enter fullscreen mode Exit fullscreen mode

This ensures atomic updates without race conditions.


4. Using Thread-Safe Collections

Using collections such as:

  • ConcurrentHashMap
  • CopyOnWriteArrayList

can help prevent race conditions in shared data structures.


Key Points to Remember

  • Race conditions occur in multithreaded environments
  • They happen when multiple threads access shared data simultaneously
  • The final result becomes unpredictable
  • Proper synchronization techniques are required to avoid them

Real-World Examples

Race conditions can occur in:

  • Bank transaction systems
  • Stock trading platforms
  • Web server request handling
  • Concurrent data processing systems

Preventing race conditions is essential for building reliable and scalable applications.


🚀 Master Advanced Java Concurrency and System Design

Concepts like Race Conditions, Thread Safety, ConcurrentHashMap, ExecutorService, ReentrantLock, and Multithreading are essential for designing high-performance backend systems.

If you want to learn these advanced concepts with real-time projects and industry-level examples, explore:

👉 Top System Design with Java Online Training

Top comments (0)