Learnitweb

Race condition in Java with examples

Race condition is the condition in which multiple threads try to access the same data or perform some operation on the data, and the correctness of the result depends on the timing of execution of the threads.

In race condition, if the timing of the threads is changed then the result may not be correct. For example, check-then-act situations most commonly may lead to race conditions if threads are not synchronized properly.

Read-Modify-Write – Example of unsafe operation and race condition

In this piece of code it looks like ++count is atomic, but it is not. This small statement (++count) is actually made up of three steps:

  • Read the count
  • Increase the count
  • Update the count
public class RaceConditionExample {

	private long count = 0;

	public long getCount() {
		return count;
	}

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

If the threads are not synchronized properly, it is quiet possible that all thread access the value at the same time and threads get the incorrect or stale results.

This piece of code gives right results when threads execute and access in right order and their timing is right. So the correctness of result is dependent on the timing of the threads. This is an example of race condition.

Race Condition in lazy initialization (check-then-act)

Here, we are creating an instance of class LazyInitialization. We are creating an instance when getInstance method is called. In getInstance method, we first check if the instance already exists or not.

public class LazyInitialization {
	private LazyInitialization instance = null;

	public LazyInitialization getInstance() {
		if (instance == null)
			instance = new LazyInitialization();
		return instance;
	}
}

It is possible in multi-threaded environment, more than one thread may execute the check condition (instance == null) and both pass the check successfully. In such case, both these threads will be able to create a new instance. Which is not the desired result.

In this example, the correctness of the result depends of the timing of the threads. This is another example of race condition.

Preventing race condition

Race condition will not occur if the operations are atomic, that is, either all should perform or none. We have already explained that ++counter is made up of three steps: read the count, increase the count and update the count.

If this operation is atomic, then all these steps will be performed before another thread tries to execute ++count or none of these steps will be performed. If this operation is atomic, then it will not be possible that if one thread is executing step 2 and another thread is able to execute any of these steps.

  1. You synchronize(using synchronized block) the critical block or unsafe code.
  2. There is another way, if the state of the object is represented by a variable. In the case of count (our first example), you can use variable classes provided in java.util.concurrent.atomic package.
private final AtomicLong count = new AtomicLong(0);