javasingletonstatic-order-fiasco

Does Java have the static order initialisation fiasco?


A recent question here had the following code (well, similar to this) to implement a singleton without synchronisation.

public class Singleton {
    private Singleton() {}
    private static class SingletonHolder { 
        private static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

Now, I think understand what this is doing. Since the instance is static final, it's built long before any threads will call getInstance() so there's no real need for synchronisation.

Synchronisation would be needed only if two threads tried to call getInstance() at the same time (and that method did construction on first call rather than at "static final" time).

My question is therefore basically: why then would you ever prefer lazy construction of the singleton with something like:

public class Singleton {
    private Singleton() {}
    private static Singleton instance = null;
    public static synchronized Singleton getInstance() {
        if (instance == null)
            instance = new Singleton();
        return instance;
    }
}

My only thoughts were that using the static final method may introduce sequencing issue as in the C++ static initialisation order fiasco.

First off, does Java actually have this problem? I know order within a class is fully specified but does it somehow guarantee consistent order between classes (such as with a class loader)?

Secondly, if the order is consistent, why would the lazy construction option ever be advantageous?


Solution

  • Now, I think understand what this is doing. Since the instance is static final, it's built long before any threads will call getInstance() so there's no real need for synchronisation.

    Not quite. It is built when the SingletonHolder class is initialized which happens the first time getInstance is called. The classloader has a separate locking mechanism, but after a class is loaded, no further locking is needed so this scheme does just enough locking to prevent multiple instantiation.

    First off, does Java actually have this problem? I know order within a class is fully specified but does it somehow guarantee consistent order between classes (such as with a class loader)?

    Java does have a problem where a class initialization cycle can lead to some class observing another class's static finals before they are initialized (technically before all static initializer blocks have run).

    Consider

    class A {
      static final int X = B.Y;
      // Call to Math.min defeats constant inlining
      static final int Y = Math.min(42, 43);
    }
    
    class B {
      static final int X = A.Y;
      static final int Y = Math.min(42, 43);
    }
    
    public class C {
      public static void main(String[] argv) {
        System.err.println("A.X=" + A.X + ", A.Y=" + A.Y);
        System.err.println("B.X=" + B.X + ", B.Y=" + B.Y);
      }
    }
    

    Running C prints

    A.X=42, A.Y=42
    B.X=0, B.Y=42
    

    But in the idiom you posted, there is no cycle between the helper and the singleton so there is no reason to prefer lazy initialization.