【问题标题】:How to create a true singleton in java?如何在java中创建一个真正的单例?
【发布时间】:2010-12-02 11:18:09
【问题描述】:

当我在多个类加载器中使用单例时,我遇到了问题。例如,由多个 EJB 访问的单例。有没有办法创建一个在所有类加载器中只有一个实例的单例?

我正在寻找使用自定义类加载器或其他方式的纯 Java 解决方案。

【问题讨论】:

  • 你用的是什么应用服务器?
  • 您的 PR 是在为自己的博客做公关吗?这就是你的个人资料所说的。所以“我找到了一个链接”实际上是“我写了那篇文章”。
  • 可能......不过,不管提出这个问题的动机如何,它仍然是一个有效的问题。
  • 我只是想证明我无法根据某人的文章“Absolute Singleton”创建绝对单例。如果这是问题,我可以删除链接。

标签: java singleton absolute


【解决方案1】:

唯一的方法是让您的单例类由单个类加载器加载 - 例如,将该 jar 文件放在引导类路径中。

静态变量本质上与加载包含该变量的类的类加载器相关联。这就是它的工作方式。如果您绝对需要一个实例,则需要该类仅由一个类加载器加载。

【讨论】:

  • 我们使用 Glassfish 应用服务器和 OpenESB 作为企业服务总线。这个单例(JCS 缓存的包装器,用于存储 Ldap 搜索结果)是公共库的一部分,在多个绑定组件和 EJB 之间共享。
  • @rjoshi:单例是否被多个类加载器加载,你能避免这种情况吗?如果该类总是在多个类加载器中,我看不出如何避免它不是“正确的”单例。
  • 并且还要确保你小心序列化/反序列化。最佳实践可能是避免让您的单例可序列化。
  • @Pascal:接受答案是错误的。所以我把它删除了。
【解决方案2】:

JavaEE 应用服务器通常通过将单例设置为“服务”来解决此问题,其确切定义和配置取决于相关应用服务器。

例如,在 JBoss 中,您可以使用 xyz-service.xml 描述符来设置挂在 JNDI 或 JMX 树之外的单例对象,并且您的应用程序组件(例如您的 EJB)将从树中获取单例。这在一定程度上保护了您免受底层类加载器语义的影响。

【讨论】:

  • 我们使用 Glassfish 应用服务器和 OpenESB 作为企业服务总线。这个单例是公共库的一部分,在多个绑定组件和 EJB 之间共享。
  • 好的,了解如何在 Glassfish 中创建 JMX 服务,并从您的组件连接到该服务。
  • 我用谷歌搜索,但除了 Glassfish V3 中的单例功能支持 EJB3.1 外,什么也找不到。但我们使用 EJB3 和 Glassfish 2.1GA。
【解决方案3】:

J2EE 在设计时考虑到了集群,因此它支持的任何设计都必须与多个 JVM 一起工作。我从您的问题中得知您不关心集群环境,因此只需简单地插入应用服务器上的 JNDI 即可。在 Glassfish 中,这称为lifecycle listener。甚至在启动之后,将你的单例插入到 JNDI 中,然后让其他一切都执行 JNDI 查找以找到它。

请注意,GlassFish 在这里仍然可能会搞砸您,因为它可能会将类序列化为 JNDI,从而导致您获得不同的实例。我怀疑它实际上是在一个 JVM 中做到这一点的,但在你尝试之前你不会知道。

真正的底线答案是 J2EE 对全局真正的单例怀有敌意,而 J2EE 解决问题的方法是重新考虑解决方案。 J2EE 方式类似于保存值的数据库或其他可以确保仅存在一个数据实例(即使表示数据的对象有多个实例)的外部服务。

【讨论】:

  • 我正在使用这个单例来存储 ldap 缓存。我想知道其他开发人员是如何解决这个问题的?
  • rjoshi,我想作为一个单独的问题提出一些更多技术细节,但在我看来,您不需要它是真正的全球性的。把它放在 JNDI 中,如果给定的查找发生两次,就这样吧。
  • @Yishai:谢谢。给你的建议。我会试一试,让你知道。我试图避免使用 JNDI/EJB 包装器并使用类加载器破解来解决,但我想这是唯一的方法..
【解决方案4】:

要实现 TRUE Singleton,您必须遵循以下准则:

  1. 将您的班级设为final。其他人不能对其进行子类化并再创建一个实例
  2. 将您的单例实例设为private static final
  3. 提供private constructorpublic getInstance()方法。
  4. 确保Singleton 类由one ClassLoader only 加载
  5. 覆盖readResolve() 方法并返回相同的实例,而无需在反序列化过程中创建新实例。

示例代码:

final class  LazySingleton {
    private LazySingleton() {}
    public static LazySingleton getInstance() {
        return LazyHolder.INSTANCE;
    }
    private static class LazyHolder {
        private static final LazySingleton INSTANCE = new LazySingleton();
    }
    private Object readResolve()  {
        return LazyHolder.INSTANCE;
    }
}

请参阅以下 SE 问题了解更多详情:

What is an efficient way to implement a singleton pattern in Java?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-29
    • 1970-01-01
    • 1970-01-01
    • 2019-06-14
    • 2018-06-02
    • 2023-02-16
    相关资源
    最近更新 更多