If lazy loading is required , I would stick with a double check instead of a synchronized method. In the end, the problem is volatile vs. synchronized :
Volatile, simply forces all accesses (read or write) to the volatile variable to go to main memory, effectively storing the volatile variable from the CPU caches. This can be important for some actions, when it is just required that the variable is visible and the access order is not important.
After the instance is initialized, the synchronized block will not be executed (but for race conditions). The only concurrency cost to pay is a one-time reading of the volatile variable.
Note that Effective Java Bloch says loading a volatile field into a local variable improves performance (I understand this because the number of volatile reads is less)
public static ProcessManager getInstance() throws Exception { ProcessManager result = singleton; if (result == null) { synchronized (MyClass.class) { result = singleton; if (result == null) { singleton = result = new ProcessManager(); } } return result; }
source share