Yes, this is thread-safe and lazy.
First, let's look at whether it's thread-safe:
- Can two threads assign a reference to
NAME_INDEX_MAP
? No. That happens in the static initializer, which is only executed when the class is initialized (JLS 8.7). Class initialization uses synchronization to ensure that only one thread will execute the initializer (JLS 12.4.2).
- Is the initial publication safe? Yes. The JLS is actually a bit fuzzy on this, but the VM definition (JVMS) is explicit:
1: Synchronize on the initialization lock, LC, for C. ...
...
4: If the Class object for C indicates that C has already been initialized, then no further action is required. Release LC and complete normally.
...
A Java Virtual Machine 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 Java memory model, all happens-before orderings (JLS §17.4.5) that would exist if the lock were acquired, still exist when the optimization is performed.
(JVMS 5.5, emphasis added)
- Will the object be modified after publication? No. It is wrapped in an unmodifiableMap, with no other reference to the underlying, modifiable HashMap.
So, is it lazy? Yes; class initialization only happens immediately before any members of that class are accessed for the first time (JLS 12.4.1), and in this case, you have only the one field. So the initialization will only happen immediately before the first time you access NAME_INDEX_MAP
, which is the laziness you want.