While reading Java Concurrency in Practice book by Brian Goetz, I came across Data races and Race conditions.
Data races
A program is said to have a data race, and therefore not be a "properly synchronized" program, when there is a variable that is read by more than one thread, written by at least one thread, and the write and the reads are not ordered by a happens-before relationship.
Race condition
A race condition occurs when the correctness of a computation depends on the relative timing or interleaving of multiple threads by the runtime; in other words, when getting the correct answer relies on lucky timing. The most common type of race condition is check-then-act, where a potentially stale observation is used to make a decision on what to do next
As I understand, Data race can be avoided by making sure that one or more of the above conditions hold false - ie, by making shared variables immutable or by making the access to them properly synchronized.
My question is about the example of a SingletonFactory that is usually given to illustrate race condition.
e.g.:
public class SingletonFactory {
private Singleton singleton = null;
private SingletonFactory() {}
public Singleton getInstance() {
if(this.singleton == null) {
this.singleton = new Singleton();
}
return this.singleton;
}
}
Can this code can also be considered to cause a Data race?
I understand that one way to make the above program "completely thread safe" would be to have a double checked locking and also make the class variable volatile.
But in case I just declare the Singleton variable volatile, but fail to synchronize the code block that initializes the variable, then can it be considered as safe at-least w.r.t "Data race", but still unsafe w.r.t. race condition? In general I am still in search of a good realistic example where there is no data race, but there is still a potential race condition!
(a blog that is usually referred to explain the difference between data race and race condition does not help me to understand this)