I don’t think that the name “eager singleton” is justified.
Consider JLS §12.4.1., When Initialization Occurs
A class or interface T will be initialized immediately before the first occurrence of any one of the following:
T
is a class and an instance of T
is created.
- A
static
method declared by T
is invoked.
- A
static
field declared by T
is assigned.
- A
static
field declared by T
is used and the field is not a constant variable (§4.12.4).
So, the initialization and in turn, the instantiation of Singleton
will happen when the method getInstance()
is invoked for the first time or, since you made the field public
, when the field is accessed for the first time, whichever comes first. But not earlier. In other words, this initialization is already as lazy as all other attempts to perform lazy initialization try to achieve.
The safety of this initialization is given by JLS §12.4.2, Detailed Initialization 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. The procedure for initializing C
is then as
follows:
- Synchronize on the initialization lock,
LC
, for C
. This involves waiting until the current thread can acquire LC
.
- If the
Class
object for C
indicates that initialization is in progress for C
by some other thread, then release LC
and block the current thread until informed that the in-progress initialization has completed, at which time repeat this step.
- If the
Class
object for C
indicates that initialization is in progress for C
by the current thread, then this must be a recursive request for initialization. Release LC
and complete normally.
- If the
Class
object for C
indicates that C
has already been initialized, then no further action is required. Release LC
and complete normally.
…
This is the procedure, every resolution of a reference to the class has follow, acquiring the unique initialization lock of the class and releasing it when the class is already initialized or the current thread is the thread performing the initialization. This lock ensures the thread safety, regardless of whether the field has been declared final
or not, as long as it is only written during the class initialization.
The reason why it still is the most efficient way to implement a singleton is given in the same chapter:
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.
Since each class is initialized only once and then used in this initialized state for a very long time compared to its initialization time, this optimization has a very high impact and therefore, is state of the art since the first Java versions. But, as this note says, the optimization must not subvert the thread safety
synchronized
, we're not talking about thread safetiness. They are a multitude of examples on the web , like : journaldev.com/1377/… – Melchior