What is Race Condition?
A race condition occurs when there are two or more threads accessing and then changing some shared data at the same time, and the final result depends on the timing of how the threads are scheduled. Because thread scheduling algorithms can swap between threads at any point of time, you don’t know the order in which these threads will attempt to access the shared data.
Its a special condition that occur inside a critical section. A critical section is a piece of code that is executed by multiple threads concurrently and where the sequence of execution of the threads makes a difference in the result of the concurrent execution of the critical section.
Race conditions may lead to unpredictable outputs and subtle programming bugs.
How to find Race Condition in Java?
Finding Race conditions in any language is a difficult task, since readability of Java code is very good and well constructed to find race conditions by code review.
Finding race conditions by unit testing is not reliable because of random nature of race conditions. There may be the chances that your unit test may passed without facing any race condition. The best way to find race condition is reviewing code manually or using code review tools which notify you about the race conditions based on code pattern and use of synchronization in Java.
Sometimes doing load testing in production environment may helps to expose race conditions in java.
How to Prevent a Race Condition?
To prevent race condition from occurring you must make sure that the critical section is executed as an atomic instruction.
A thread can prevent this from happening by locking an object. When an object is locked by one thread and another thread tries to call a synchronized method on the same object, the second thread will block until the object is unlocked.
By proper thread synchronization in critical sections, you can avoid race conditions in your application. Thread synchronization can be achieved in java by using a synchronized block on your code. Thread synchronization can also be achieved using other synchronization methods like locks(Re-entrant Lock) or atomic variables like java.util.concurrent.atomic.AtomicInteger.