如果使用INSTANCE holder类来实现Singleton Pattern,编译器会生成另一个名为classname$1.class的类文件。您可以通过此链接找到问题:here 。所以我的问题是单例模式的解决方案是什么?再检查一遍?
4 回答
您似乎在问这个单例代码是如何实现线程安全初始化的:
public final class Test {
static final class TestHolder {
private static final Test INSTANCE = new Test();
}
private Test() {}
public static Test getInstance() {
return TestHolder.INSTANCE;
}
}
该解决方案的线程安全性得到保证,因为:
- 静态初始化以线程安全的方式执行,并且
- 一旦初始化,
final
变量就可以在没有进一步同步的情况下使用。
它没有进行双重检查锁定。
请注意,此特定模式是线程安全的并且是惰性初始化的。如果您想要具有非延迟初始化的线程安全单例,那么:
public final class Test {
private static final Test INSTANCE = new Test();
private Test() {}
public static Test getInstance() {
return INSTANCE;
}
}
“enum
作为单例”的方法也是可以接受的……尽管工程师 Dollery 这么说。
使用双重检查锁定的延迟初始化可以从 Java 5 开始正确实现(如果您使用 a 正确实现volatile
),但在 Java 1.4.x 和更早版本中,这种习惯用法被破坏了。
您可以使用简单的枚举。
public enum Singleton {
INSTANCE;
}
您可以INSTANCE
简单地访问 asSingleton.INSTANCE
而不是调用getInstance()
方法。默认情况下,创建 Enum 实例是线程安全的。
根据您的部署架构,在 Java 中实现单例可能会变得非常困难。当您谈论单例时,您还必须谈论它们的范围/上下文。因为很少有人知道这种单例通常被称为邪恶并被认为是反模式。
如果您要部署到应用程序服务器之外的单个 jvm,那么惯用方法是在其内部实现类的私有最终静态实例,并通过适当命名的方法公开它(getInstance() 很常见),然后类私有的构造函数。这是一个受限于单个类加载器和单个 jvm 的单例。
然而,这种情况非常罕见。
在所有其他情况下,您必须考虑类加载器问题以及将代码部署到多个 jvm/服务器的问题。在典型的 Java EE 应用程序容器中,每个战争通常使用自己的类加载器加载。如果每场战争都依赖于一个包含单例的罐子,那么他们每个人都会得到自己的单例副本。这可能没问题,如果单例用于数据库连接,并且您可以使用其中的很多。但是,如果单例应该保护对有限资源的访问,那么这将不会很好地工作,因为您将拥有一个能够访问每个 webapp 资源的类。您的单例的范围是容器范围。
在具有集群和实时故障转移的完整企业部署中,情况会变得更糟。在这种环境中,您有许多软件副本同时运行。很难想象一个只有 Java 的单例在这个范围内工作——企业范围。在这个级别,您需要弄清楚单例创建或执行的单一事实来源(如果可以有多个实例但一次只有一个实例可以处理请求),以及您创建的所有单例同一类型必须遵循单一的事实来源——企业信号量。
我会在 jdk 1.5 之后使用枚举。检查htis链接了解详情:http: //javarevisited.blogspot.in/2012/07/why-enum-singleton-are-better-in-java.html
- 不建议进行双重检查锁定,它不会提供性能。
- 您提到的链接中的解决方案是使用静态内部类的有效解决方案,因为它不使用同步并使用延迟加载,但枚举与性能一起最容易实现。
下面的文章解释了为什么双重检查锁定可能是一个糟糕的选择[正如 Thomas 指出的,下面文章中提到的双重检查锁定问题已在 Java 1.5 中得到修复]:http ://www.ibm.com/developerworks/java/library/ j-dcl/index.html