I am trying to write an instance method to lazily initialise several static variables. The objects I am initialising are immutable, and the references for the objects are not changed by any other instance or static methods in the class. I want the initialization code to never be executed more than once even though there may be several instances of the class in many different threads. The initialization needs to take place in an instance method as the method overrides a method in a superclass. My approach I am using is as follows.
private static volatile boolean isPrepared;
private static volatile Object object1;
private static volatile Object object2;
private static volatile Object object3;
@Override
void prepare() {
synchronized (this.getClass()) {
if (isPrepared) { return; }
object1 = expensiveCalculation1();
object2 = expensiveCalculation2();
object3 = expensiveCalculation3();
isPrepared = true;
}
}
I am assuming that since the initialization takes place in a single synchronized block, it would be impossible for an instance to ever observe isPrepared as being true unless object1, object2 and object3 are all non-null. I am also assuming that it wouldn't work by simply declaring prepare() as synchronized as the lock would just be this. Are my assumptions right? Also, is it a good idea to have several variables marked volatile when you want to regard them as being initialised together, or should I bundle them together into a single Immutable class?
Bundling all lazily-initialized state into an immutable object is usually the preferred approach because then all you need is a
volatilevariable, with no synchronization. That arrangement opens you to some duplicated effort if another thread starts initializing while initialization is in progress, but the chances of that can be minimized, such as by writing a sentinel value to thevolatileto signal the "in progress" state.