The initialization of enum
constants happens in the class initializer, similar to had you written
static final Singleton instance = new Singleton();
or
static final Singleton instance;
static {
instance = new Singleton();
}
The safety comes from the fact that the JVM perform class initialization under a JVM specific lock:
Because the Java programming language is multithreaded, initialization of a class or interface requires careful synchronization, since some other thread may be trying to initialize the same class or interface at the same time. … The implementation of the Java Virtual Machine is responsible for taking care of synchronization and recursive initialization by using the following procedure.
…
For each class or interface C
, there is a unique initialization lock LC
. The mapping from C
to LC
is left to the discretion of the Java Virtual Machine implementation.
…
I left out a lot of technical details from the specification, as to a Java programmer, the most important point is that there is a safety mechanism implemented by every conforming JVM. The end of the section has the comment:
An implementation may optimize this procedure by eliding the lock acquisition in step 1 (and release in step 4/5) when it can determine that the initialization of the class has already completed, provided that, in terms of the memory model, all happens-before orderings that would exist if the lock were acquired, still exist when the optimization is performed.
This is, of course, an important point of this implementation of the singleton pattern, that later access to the static final
field does not need to acquire a lock. Since all classes go from the uninitialized to the initialized state exactly once and it affects every operation (including all other possibilities to implement the singleton pattern), you can expect every JVM to do this fundamental optimization. Even if a particular JVM does not do this, the static final
field would be the fastest lazy singleton implementation on that virtual machine…