What is the best solution for Singleton Pattern?
如果使用实例持有者类实现单例模式,编译器将生成另一个名为classname$1.class的类文件。您可以通过以下链接找到问题:这里.所以我的问题是,单例模式的解决方案是什么?重复检查?
您似乎在问这个singleton代码是如何实现线程安全初始化的:
1 2 3 4 5 6 7 8 9 10 11
| 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变量。
它没有进行双重检查锁定。
注意,这个特殊的模式是线程安全的,并且是惰性初始化的。如果您希望使用非延迟初始化的线程安全单例,那么:
1 2 3 4 5 6 7 8 9
| public final class Test {
private static final Test INSTANCE = new Test();
private Test() {}
public static Test getInstance() {
return INSTANCE;
}
} |
也可以采用"单体"方法。不管多莉工程师怎么说。
使用双检查锁定的懒惰初始化可以从Java 5向前正确地执行(如果使用EDCOX1 OR 5表示正确),但是在Java 1.4 .x和更早的时候,习惯用语被打破。
- 如果类测试有一些静态方法,实例也将被初始化。
- 我不明白你的意思。你指的是哪个版本?
- 如果您在测试中有一个静态方法,那么调用该方法,将初始化实例,因为类已加载。
- 这对于第二个(非惰性)示例是正确的。如果您不希望发生这种情况,请不要将(其他)静态方法放在单例类中…或者不要叫他们:-)。但是在第一个(lazy)示例中,对外部类中其他静态方法的调用不会触发内部类的静态初始化。只有当getInstance()方法尝试访问内部类静态字段时,才会触发内部类初始化。
您可以使用简单的枚举。
1 2 3
| public enum Singleton {
INSTANCE;
} |
您可以简单地将INSTANCE作为Singleton.INSTANCE访问,而不是调用getInstance()方法。默认情况下,创建枚举实例是线程安全的。
- 是的,这是一个解决方案,但有点有线。
- @handrenliang这比拥有一个private static final Type instance = new Type();和一个public static Type getInstance() { return instance; }要好(或者其他实现单例的方法)。
- 但是枚举不能在Java中扩展枚举或类
- @路易吉——这一点都不好,这是误导和错误的。这不是Enums的用途,你真的不应该这样做。
- @韩德良也许能帮上忙。
- @工程小车的问题是关于单体的设计。从一个单一的扩展给你坏的设计的想法。注意,这也是enum存在的原因:为了防止这种糟糕的设计。在这种情况下,OP最好解释他/她的实际问题,而不是问我如何使单例模式工作?
- 枚举根本不存在。去读JLS,然后再给我回电话。
- @EngineerDollery JLS不会告诉您这一点,您可以从应用程序设计经验中学到这一点。
- jls告诉我枚举是什么。它甚至没有暗示你可以或者应该把它作为一个单例。我知道这是因为我经常读。很难想象任何人,除了Enums的设计者之外,还会想到这样的事情。
- 另请参见stackoverflow.com/questions/5759596/…
根据部署架构,在Java中实现单体变得非常困难。当你谈论单身时,你也必须谈论他们的范围/背景。因为很少有人理解这种单子常被称为邪恶,被认为是反模式。
如果要部署到应用服务器外部的单个JVM,则惯用方法是在其内部实现类的私有最终静态实例,并通过适当命名的方法(getInstance()是常见的)公开该实例,并使类的构造函数成为私有的。这是一个单实例,约束为一个类加载器和一个JVM。
然而,这种情况非常罕见。
在所有其他情况下,您必须考虑类加载器问题以及将代码部署到多个JVM/服务器的问题。在典型的JavaEE应用程序容器中,通常使用自己的类加载器加载每个战争。如果每一场战争都依赖于一个包含单重子的罐子,那么他们每个人都会得到自己的单重子副本。如果singleton是用于,比如说,一个DB连接,并且允许您使用很多数据库连接,那么这就可以了。但是,如果单例程序应该保护对有限资源的访问,那么这不会很好地工作,因为每个webapp都有一个类能够访问该资源。这里的singleton的作用域是容器作用域。
在完整的企业部署中,随着集群和实时故障转移,情况变得更糟。在这种环境中,您有许多同时运行的软件副本。很难想象一个Java独生子在这个范围内工作——企业范围。在这个层次上,您需要弄清楚单例创建或执行的单一真相来源是什么(如果可以有多个实例,但一次只能有一个实例处理请求),并且您创建的同一类型的所有单例都必须遵从单一真相来源——企业信号量。
- 在所有这些解释之后,实现singleton似乎是一个坏主意…
- 嗯,是的。Spring在您创建的所有bean上默认使用了一个名为singleton的东西,但是它们很清楚地定义了单个应用程序上下文的作用域。
- Spring没有显式地使用这种类型的singleton,这意味着它将保留托管bean的对象引用的单个实例,因此如果取消部署应用程序,这些实例也将从JVM中删除。
- 这根本不是什么意思。您可以自由地将对SpringBean的引用保留在应用程序上下文之外,并且无论Spring多么努力,都无法从Spring中删除实例。另外,它们也不会从JVM中移除——类装入器很少在类装入后移除类。Spring所做的只是尝试删除对它所知道的bean的引用,而不是其他任何东西。
- 同样,SpringSingleton是关于拥有一个托管bean实例的,它不是您可能认为的static final实例。顺便说一下,我在上一次评论中从来没有提到过卸载类。
- 我没有说春天使用静态决赛。我不认为我说过,让我回顾一下我的评论…不,从来没有暗示过。
- 那么,如何解释Spring有一个类的实例,该实例只有在基于该注释卸载类时才会被移除?
- 如果你有一个明确的问题,请写一个问题——这是一个讨论,这不是讨论的地方。
我会在JDK1.5之后使用Enum。有关详细信息,请查看htis链接:http://javarevisited.blogspot.in/2012/07/why-enum-singleton-are-better-in-java.html
不建议双重检查锁定,它不能提供性能。
您提到的链接中的解决方案是一个使用静态内部类的有效解决方案,因为它不使用同步并使用延迟加载,但枚举与性能一起最容易实现。
下面的文章解释了为什么双检查锁定可能是一个坏的选择[正如托马斯指出的那样,BEOW文章中提到的双重检查锁定问题是用Java 1.5固定的:]:HTTP://www. iBM/CuxeWorks/Java/Labaly/J-DCL/NETX.HTML
- +1解释针对单例模式实现使用enum的好处。
- 但是EnUM无法在Java中扩展枚举或类。
- 你想做什么?你想扩展一个类并使它成为单例吗?看起来设计不好。
- 这是一个可怕的想法。你完全错了。你不能把这样一个愚蠢的想法建立在一篇博文上!你知道什么是模式吗?你听说过成语吗?
- @如果你解释你的实际问题,而不是问如何实现一个单例模式,那就更好了。
- @EngineerDollary不能扩展enum,但是enum值可以扩展override定义中声明的方法。
- JeWorkDoLoLe: Joshua Bloch的JavaJava讲述了使用EnUM的SuntLon。这是一个标准参考。
- Josh Bloch的书是一个标准的参考,你是对的,但是Josh在很多次之前都错了,而且是不可感染的,而且他并没有被认为是Java的Idothon使用的专家(事实上,如果你听从他的建议,我们就会有吸气剂和定位器无处不在,DDD就不存在了)。枚举是枚举,而不是单例枚举。当开发人员看到一个枚举时,他们认为它是一个枚举,而不是一个单例——它不是一个习语,它只是一个普通的哑巴。
- @我知道有很多实现单例模式的方法,我只想知道在大多数情况下哪一种是最好的。
- @handrenliang最好不要使用singleton,使用enum或第三方代理来为您处理单一注册,比如spring或ejb @Singleton。
- @我同意你的看法。当使用枚举时,您可能会依赖注释来解释这是一个单例模式,而不是真正的枚举。
- 我在这个行业35年来听到的最愚蠢的想法之一就是用枚举来表示单件。如果一个为我工作的程序员这样做的话,我们会非常认真地讨论他们的工作资格——即使是乔希·布洛克——而且我有很多为我工作的程序员。我从来没有见过有人在创建一个单例时尝试过如此误导性的东西(事实上,在我的团队中,单例是一个反模式,因为它会导致麻烦)
- @EngineerDollary这个单例类甚至不能被继承的事实使它成为一个很好的例子,可以被enum替换。如果没有,那么说明为什么不能这样做(注意:对于当前操作的代码示例)。
- @除了"它是愚蠢的"之外,你还有什么争论吗?我认为您可以将单例看作枚举的特殊情况,它只有一个值。洛基在回答中解释了优势。
- 您引用的developerWorks文章"为什么双重检查锁定被破坏"已经严重过时(它是从2002年开始的)。在2004中用Java 1.5固定双重检查锁定。从那以后,如果做得好,没关系。