What is a race condition and how can it be prevented in Java?

A race condition occurs in a multithreaded environment when multiple threads access and modify the same shared resource at the same time, and the final result depends on the order in which the threads execute. Because thread scheduling is unpredictable, race conditions can lead to inconsistent data, unexpected behavior, and difficult-to-debug issues.

Key Points: • Race conditions occur when shared mutable data is accessed concurrently without proper synchronization. • The outcome may vary from one execution to another, making the application unreliable. • They can be prevented using synchronization, Lock implementations, Atomic classes, or thread-safe concurrent collections.

Example: Consider a banking application where two threads attempt to withdraw money from the same account simultaneously. Without proper synchronization, both threads may read the same balance and perform incorrect updates, resulting in an invalid account balance.

Code Example:

class Counter {

    private int count = 0;

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

    public int getCount() {
        return count;
    }
}

public class RaceConditionDemo {

    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.getCount());
    }
}

Interview Tip: A concise interview answer is: A race condition occurs when multiple threads simultaneously access and modify shared data, causing unpredictable results. It can be prevented using synchronization, locks, Atomic classes, or concurrent collections to ensure thread-safe access to shared resources.