Java 用于创建单例实例的工厂方法
Java 用于创建单例实例的工厂方法,java,multithreading,Java,Multithreading,我已经在静态工厂方法中编写了以下代码来返回DefaultCache的单个实例 public static ICache getInstance() { if (cacheInstance == null) { synchronized (ICache.class) { if (cacheInstance == null) { cacheInstance = new DefaultCache(); } }
我已经在静态工厂方法中编写了以下代码来返回DefaultCache的单个实例
public static ICache getInstance() {
if (cacheInstance == null) {
synchronized (ICache.class) {
if (cacheInstance == null) {
cacheInstance = new DefaultCache();
}
}
}
return cacheInstance;
}
我们真的需要对同步块内的cacheInstance进行第二次空检查吗?有一个私有构造函数来避免所有这些检查
class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {
}
public static final Singleton getInstance() {
return instance;
}
}
假设
- 惰性初始化:使用实例填充singleton字段 在第一次使用时(如您的示例中)
- (标准)初始化:singleton字段填充一个 JVM加载类后立即执行
//class declaration
static {
cacheInstance = new DefaultCache();
}
或者在你声明的同一行
private static final DefaultCache cacheInstance = new DefaultCache();
我将避免使用延迟初始化的单例进行争用检查:
public class Singleton {
public static ICache getInstance() {
return LazyCacheInitializer.INSTANCE;
}
private class LazyCacheInitializer {
private static final ICache INSTANCE = new DefaultCache();
}
}
您需要进行第二次检查,因为在尝试获取锁时,该值可能已由另一个线程设置。事实上,在进入同步块之前,您没有该值的安全视图。它可能是由另一个线程在任何时间之前设置的 最简单的惰性单例是使用枚举
public enum DefaultCache implements ICache {
INSTANCE
}
我假设您没有这样做,以便可以更改实现
顺便说一句:我建议您只在可能的情况下使用无状态单例,并尽可能对所有有状态对象使用依赖注入。是的,否则可以创建两个以上的实例。假设您有多个线程。第一个测试是使用races条件完成的,即一些线程同时将变量视为null,并尝试设置实例。如果不进行第二次检查,每个线程将创建一个新实例。您始终可以使用基于
enum
的单例模式实现,如Peter posted,但请记住,您的所有实现都是单例每个类加载器
您还必须注意
Cloneable
接口。这比使用enum
有什么好处?@Peter嗯,enum的缺点是其序列化模型。枚举仅序列化其名称,而不序列化其状态。如果单身汉可以有任何一种状态,它就会丢失。因此,状态为的枚举不能通过网络发送,也不能从磁盘保存和恢复。不确定在这种特殊情况下考虑序列化单例缓存是否有帮助,但您的问题是关于优点/缺点。虽然同意您的观点,但您必须对此代码进行大量更改才能使用反序列化缓存,因此我不确定这是否更好。@Peter可能,但这不是问题,也不是你的问题。简单地说,我的观点是,enums在这方面会遇到麻烦。除了答案之外,还可以看看c2.com/cgi/wiki?SingletonsReeville您可能想阅读维基百科的文章