我找到了三种实例化Singleton的方法,但我怀疑它们中是否有一种是最好的。我在多线程环境中使用它们并且更喜欢懒惰的实例化 样本1:
private static final ClassName INSTANCE = new ClassName();
public static ClassName getInstance() {
return INSTANCE;
}
样本2:
private static class SingletonHolder {
public static final ClassName INSTANCE = new ClassName();
}
public static ClassName getInstance() {
return SingletonHolder.INSTANCE;
}
样本3:
private static ClassName INSTANCE;
public static synchronized ClassName getInstance()
{
if (INSTANCE == null)
INSTANCE = new ClassName();
return INSTANCE;
}
我使用ATM的项目到处都使用Sample 2,但我更喜欢Sample 3。还有Enum版本,但我还没有得到它。
这里的问题是 - 在哪些情况下我应该/不应该使用这些变体中的任何一种?我不是在寻找冗长的解释(那里有很多关于这个问题的其他主题,但它们最终都变成了争论IMO),我希望用几句话来理解它。
答案 0 :(得分:20)
在java中实现单例的最安全,最简单的方法是使用枚举(就像你提到的那样):
public enum ClassName {
INSTANCE;
// fields, setters and getters
}
枚举语义保证只有一个INSTANCE
如果不使用枚举方法,你必须处理很多方面,比如竞争条件和反射。我一直在打破一些框架的单例,并滥用它们,因为它们没有正确编写。枚举保证没有人会破坏它。
答案 1 :(得分:4)
样本1不使用延迟初始化。
样本2和3都是懒惰的。示例2使用具有无同步开销的Initialization on demand holder idiom (IODH)。因此它比样本3快。
在Effective Java(第3项)中,Joshua Bloch建议单元素枚举类型是实现单例的最佳方式。
但是,如果您不确定枚举类型,请坚持使用IODH。
答案 2 :(得分:2)
首先,确保您需要单身,并且您想要提供对单身人士的“全局级”访问权限。我发现在很多情况下,单身人士的客户不需要知道它是一个单身人士。相反,他们只需要在实例化时获得服务。
因此,无论您如何获得单例(如果有的话),请考虑更改类访问此对象的方式。虽然这意味着修改构造函数并改变“分布变化”,但我发现依赖注入框架可以降低成本。 DI框架也可以处理单例实例化(例如,Guice就是这样)。
除此之外。选项3是我熟悉的典型且最常见(和线程安全)版本。选项1主要用于非延迟初始化(并不总是可接受)。我从未见过2使用过。
答案 3 :(得分:1)
示例1:如果您不需要懒惰的单身人士,请使用 示例2:永远不要使用 - 太多课程会让人感到困惑。一个只用于保存一个变量的内部类似乎有点不必要。 样本3:这是一个懒惰的单身人士。如果需要,请使用它。