【问题标题】:Why enum singleton is lazy?为什么枚举单例是懒惰的?
【发布时间】:2020-09-24 17:53:36
【问题描述】:

看到answers like these,尝试通过comments澄清,对examples here不满意。

也许是时候回答这个具体问题了……

为什么将枚举单例实现称为lazy

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

它与 eager 实施有何不同?

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() {}
}

两者都将在不访问 INSTANCE/getInstance() 的情况下初始化实例 - 例如拨打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();
    }
}

输出:

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

现在,我们可以说两者都是懒惰的。那么渴望是什么?

很明显(例如)“双重检查锁定”方式实际上是惰性的(并且混乱且缓慢)。但是如果 enum 是惰性的,那么由于不可避免的类加载,any 单例也是惰性的——事实上,一切都是惰性的。这种区别在什么时候会失去意义?

【问题讨论】:

  • 我不太明白你的问题;这一切与enum 有什么关系?该类已实例化,因为您在该字段中有 new 调用,而不是(因为它更典型地用于单例)在 getInstance 中为 if (instance == null) instance = new ...
  • 您的第一个带有枚举的代码并不比第二个代码更懒惰。
  • @khelwood 是的。这就是我的观点。为什么这是提供惰性单例的答案之一?
  • 询问这些答案的作者。
  • 在这个层面上没有意义,但是类加载细节在更深层次上有点不同,静态初始化需要一个内部类来获得与enum相同的完全延迟加载效果已。但这只是理论上的,而且由于 enum 恰好使它变得简单有效,这是目前最好的方法。

标签: java enums singleton lazy-initialization double-checked-locking


【解决方案1】:

前两个链接的答案(Peter LawreyJoachim Sauer)都同意枚举不是延迟初始化的。第三个链接中的答案对于延迟初始化的含义是完全错误的。

使用枚举作为单例的建议源自 Josh Bloch 的 Effective Java。值得注意的是,关于枚举单例的章节没有提到懒惰。后面有一章专门讨论延迟初始化,同样没有提到枚举。本章包含两个亮点。

  • 如果您需要使用惰性初始化来提高静态字段的性能,请使用惰性初始化持有者类惯用语。
  • 如果您需要使用延迟初始化来提高实例字段的性能,请使用仔细检查习惯用法。

毫无疑问,如果枚举以任何方式延迟初始化,它们将是此列表中的另一个惯用语。事实上,它们不是,尽管对延迟初始化的含义的混淆会导致一些不正确的答案,正如 OP 所示。

【讨论】:

  • 很好的总结,对原始来源进行了迂腐的检查!我现在毫不怀疑“枚举”周围的“懒惰”只是一种广泛的混淆。它源于step-by-step answers like these,它(1)从简单的渴望单例的“缺点”开始,(2)提出惰性解决方案,(3)以枚举结束(好像它不渴望)。
【解决方案2】:

我可以下以下赌注吗:

您正在尝试识别 2 个“进程”或... "things"(让我们让这变得容易理解 - 因为如果我开始说“代码块”,这听起来更难)。 ..

  • 在某些时候类加载器会运行,您想知道当类加载器加载一个类时会执行什么"things"
  • 在另一个点上调用类上的方法会导致另一个 "thing" 运行/执行,您想知道究竟是哪个(哪个 "processes" strong>) 将开始..

以下事实是相关的:

  • 静态初始化程序在类加载器加载类时运行。类加载器不会加载类,直到代码 running 遇到需要加载它(因为一个方法或字段有 被调用)如:touchClass()
  • 如果 EITHER 一个类的单例实例,OR 枚举类型有一个 field 正在 >static 一旦你“触摸” 类 - 因为类加载器会在加载时为类或枚举运行所有 static initializations
  • 当方法调用询问类时,可能会发生延迟加载(这是我对您所问内容的“解释”) 创建一个单例实例 - 这可能会发生很多 classenum“加载”后的时间。

类如下:

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"); }
}

另一方面:

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() { }
}

所以下面的代码会产生输出

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

【讨论】:

    猜你喜欢
    • 2011-03-24
    • 2019-02-25
    • 2015-09-06
    • 1970-01-01
    • 2013-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-15
    相关资源
    最近更新 更多