Factory方法创建单例实例

时间:2012-12-19 15:27:03

标签: java multithreading


我在静态工厂方法中编写了下面的代码来返回DefaultCache的单个实例。

public static ICache getInstance() {
   if (cacheInstance == null) {
      synchronized (ICache.class) {
         if (cacheInstance == null) {
            cacheInstance = new DefaultCache();
         }
      }
   }
   return cacheInstance;
}

我们真的需要对同步块中的 cacheInstance 进行第二次空检查吗?

6 个答案:

答案 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;
    }
}