13

One common (1,2) way of implementing a singleton uses an inner class with a static member:

public class Singleton  {    
    private static class SingletonHolder {    
        public static final Singleton instance = new Singleton();
    }    

    public static Singleton getInstance() {    
        return SingletonHolder.instance;    
    }

    private Singleton() {
        //...
    }
}

This implementation is said to be lazily initialized and thread-safe. But what exactly guarantees its thread safety? JLS 17 that deals with Threads and Locks doesn't mention that static fields have any sort of happens-before relationship. How can I be sure that the initialization will only happen once and that all threads see the same instance?

Malt
  • 25,324
  • 9
  • 56
  • 86
  • 3
    You may want to read about [Initialization on demand holder](https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom). – RealSkeptic Oct 03 '17 at 10:10
  • the `final` is the key here, – nafas Oct 03 '17 at 10:10
  • 2
    @nafas, no, actually, the key here is that a class is loaded only when it is needed. – RealSkeptic Oct 03 '17 at 10:11
  • @RealSkeptic good point, thx – nafas Oct 03 '17 at 10:18
  • 1
    What make it a singleton ? What prevents invoking of `new Singleton();`? – c0der Oct 03 '17 at 10:41
  • @c0der The Singleton constructor (which doesn't appear in the question for brevity) should be `private`. – Malt Oct 03 '17 at 10:45
  • 1
    Obviously. I think it should be added to the code. – c0der Oct 03 '17 at 10:46
  • So the only difference between having an inner static class holding a singleton instance and simply using a static singleton member in the outer class itself is being lazily loaded? I can see why you'd need this, but surely there are better ways? – Neil Oct 03 '17 at 12:45
  • 1
    You may **also** want to read [Singleton Considered Stupid](https://sites.google.com/site/steveyegge2/singleton-considered-stupid) – T.E.D. Oct 03 '17 at 14:37
  • Also please read [What is so bad about singletons?](https://stackoverflow.com/q/137975/) –  Oct 03 '17 at 16:05

2 Answers2

9

It's well described in Java Concurrency in Practice:

The lazy initialization holder class idiom uses a class whose only purpose is to initialize the Resource. The JVM defers initializing the ResourceHolder class until it is actually used [JLS 12.4.1], and because the Resource is initialized with a static initializer, no additional synchronization is needed. The first call to getresource by any thread causes ResourceHolder to be loaded and initialized, at which time the initialization of the Resource happens through the static initializer.

Static initialization

Static initializers are run by the JVM at class initialization time, after class loading but before the class is used by any thread. Because the JVM acquires a lock during initialization [JLS 12.4.2] and this lock is acquired by each thread at least once to ensure that the class has been loaded, memory writes made during static initialization are automatically visible to all threads. Thus statically initialized objects require no explicit synchronization either during construction or when being referenced.

DDovzhenko
  • 1,115
  • 1
  • 10
  • 29
5

There are two points we need to understand first:

Static initialization happens only once when loading the class

Fields that have the static modifier in their declaration are called static fields or class variables. They are associated with the class, rather than with any object. Every instance of the class shares a class variable, which is in one fixed location in memory

....

Initialization of a class consists of executing its static initializers and the initializers for static fields (class variables) declared in the class

This means that static initializers executed only once when initializing the object class (the actual Class object , not an instance of the class).

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.

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.

Now, in simple words, when two threads try to initialize instance the first thread which acquire LC is the one that actually initialize instnace, and because it does that statically, java provides a promise that it happens only once.

For more information regarding initialization lock read JSL 17

Community
  • 1
  • 1
Gal Dreiman
  • 3,749
  • 2
  • 18
  • 34