Why enum singleton is lazy?

杀马特。学长 韩版系。学妹 提交于 2020-11-29 11:11:36

问题


I saw answers like these, tried to clarify via comments, and was unsatisfied by examples here.

Maybe it's time for this specific question...

Why enum singleton implementation is called lazy?

public enum EnumLazySingleton {
    INSTANCE;
    EnumLazySingleton() {
        System.out.println("constructing: " + this);
    }
    public static void touchClass() {}
}

How it is different from eager implementation?

public class BasicEagerSingleton {
    private static final BasicEagerSingleton instance = new BasicEagerSingleton();
    public static BasicEagerSingleton getInstance() {
        return instance;
    }
    private BasicEagerSingleton() {
        System.out.println("constructing: " + this);
    }
    public static void touchClass() {}
}

Both will init instance without accessing INSTANCE/getInstance() - e.g. call touchClass().

public class TestSingleton {
    public static void main(String... args) {
        System.out.println("sleeping for 5 sec...");
        System.out.println("touching " + BasicEagerSingleton.class.getSimpleName());
        BasicEagerSingleton.touchClass();
        System.out.println("touching " + EnumLazySingleton.class.getSimpleName());
        EnumLazySingleton.touchClass();
    }
}

Output:

sleeping for 5 sec...
touching BasicEagerSingleton
constructing: BasicEagerSingleton@7bfcd12c
touching EnumLazySingleton
constructing: INSTANCE

Now, we can say both are lazy. What is eager then?

It is clear how (e.g) "double-checked locking" way is actually lazy (and messy, and slow). But if enum is lazy, then any singleton is lazy due to inevitable class loading - in fact, everything is lazy. At which point will this distinction stop making any sense?


回答1:


The first two linked answers (by Peter Lawrey and Joachim Sauer) both agree that enums are not lazily initialized. Answers in the third link are simply wrong about what lazy initialization means.

The recommendation to use enums as singletons originates from Josh Bloch's Effective Java. Notably, the chapter on enum singletons makes no mention of laziness. There is a later chapter dedicated to lazy initialization, that likewise makes no mention of enums. The chapter contains two highlights.

  • If you need to use lazy initialization for performance on a static field, use the lazy initialization holder class idiom.
  • If you need to use lazy initialization for performance on an instance field, use the double-check idiom.

Undoubtedly, enums would be another idiom on this list if they were in any way lazily initialized. In fact they are not, although confusion about the meaning of lazy initialization results in some incorrect answers, as the OP shows.




回答2:


May I wager the following:

You are trying to identify 2 "processes" or ... "things" (let's make this easy to understand - because if I start saying "Code Blocks", it sounds more difficult)...

  • At some point the class-loader will run, and you would like to know what "things" will be executed when the class-loader loads a class.
  • At another point invoking a method on the class will cause another "thing" to run / execute, and you would like to know which, exactly, (which "processes") would start..

The following facts are relevant:

  • Static initializers are run when the class-loader loads the class. The class-loader will not load the class until the code that is running encounters the need to load it (because a method or field has been invoked) such as: touchClass()
  • If a singleton instance of EITHER a class, OR an enumerated type has a field that is being initialized in the static part of the class it will be loaded as soon as you 'touch' the class - because the Class-Loader runs all static initializations for a class or enum on loading.
  • Lazy loading, likely, (And this is my "interpretation" about what you are asking) would happen when a method invokation asks the class to create a singleton instance - which could happen quite a bit of time after the "loading" of the class or enum.

A class like the following:

public class LazySingleton
{
    // At time of class-loading, this singleton is set to 'null'
    private static singleton = null;

    // This is a method that will not be invoked until it is called by
    // some other code-block (some other "thing")...  When "touchClass()"
    // is called, the singleton instance is not created.
    public static LazySingleton retrieveSingleton()
    {
        if (singleton == null) singleton = new LazySingleton();
        return singleton;
    }

    // DOES NOTHING...  The Singleton is *not* loaded, even though the
    // Class Loader has already loaded this Java ".class" file
    // into memory.
    public static void touchClass() { }

    private LazySingleton()
    { System.out.println("constructing: LazySingleton"); }
}

Here on the other hand:

public enum EagerEnum
{
  // The class loader will run this constructor as soon as this 'enum'
  // is loaded from a '.class' file (in JAR or on disk) into memory
  MyEnumConstant();

  private EagerEnum()
  { System.out.println("Eager Enum Constructed"); }

  // This will cause the Class Loader to Load this enum from the
  // Java ".class" File immediately, and the "MyEnumConstant" will
  // also have to be loaded - meaning the constructor will be called.
  public static void touchEnum() { }
}

So the following code would produce the output

LazySingleton.touchClass();            // Prints nothing
EagerEnum.touchClass();                // Prints "Eager Enum Constructed"
LazySingleton.getSingletonInstance();  // Prints "constructing: LazySingleton


来源:https://stackoverflow.com/questions/64051855/why-enum-singleton-is-lazy

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!