我在静态工厂方法中编写了下面的代码来返回DefaultCache的单个实例。
public static ICache getInstance() {
if (cacheInstance == null) {
synchronized (ICache.class) {
if (cacheInstance == null) {
cacheInstance = new DefaultCache();
}
}
}
return cacheInstance;
}
我们真的需要对同步块中的 cacheInstance 进行第二次空检查吗?
答案 0 :(得分:6)
您需要进行第二次检查,因为在您尝试获取锁定时,该值可能已由另一个线程设置。事实上,在同步块内部之前,您没有此值的安全视图。它可以在任何时间之前由另一个线程设置。
最简单的懒惰单身人士是使用枚举
public enum DefaultCache implements ICache {
INSTANCE
}
我假设你没有这样做,所以你可以改变实施。
顺便说一句:我建议你尽可能使用无状态单例,尽可能使用依赖性注入所有有状态对象。
答案 1 :(得分:4)
我会避免使用延迟初始化的单例进行争用检查:
public class Singleton {
public static ICache getInstance() {
return LazyCacheInitializer.INSTANCE;
}
private class LazyCacheInitializer {
private static final ICache INSTANCE = new DefaultCache();
}
}
答案 2 :(得分:1)
是的,否则可以创建两个以上的实例。假设您有多个线程。第一个测试是使用races条件完成的,即某些线程将变量同时视为null并尝试设置实例。如果没有第二次检查,每个线程都将创建一个新实例。
答案 3 :(得分:0)
假设cacheInstance
是静态的,您应该考虑两种可能的初始化策略:
延迟启动:使用实例填充单例字段 在它第一次使用时(如你的例子)。
(Standarad)初始化:单例字段填充了一个 实例一旦JVM加载了类。
因此,如果您打算使用延迟启动,则应检查是否为null,否则您将只返回空指针。
或者,您也可以在静态块中初始化字段。
//class declaration
static {
cacheInstance = new DefaultCache();
}
或在您声明它的同一行
private static final DefaultCache cacheInstance = new DefaultCache();
答案 4 :(得分:0)
您可以随时使用像Peter发布的基于enum
的单例模式实现,但请记住,您的所有实现都是单个每个类加载器。
您还必须留意Cloneable
界面。
答案 5 :(得分:-1)
有一个私有构造函数来避免所有这些检查。
class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {
}
public static final Singleton getInstance() {
return instance;
}
}