5


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

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

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

4

6 回答 6

6

您需要第二次检查,因为在您尝试获取锁时,该值可能已由另一个线程设置。事实上,在您进入同步块之前,您无法安全地查看此值。它可以在任何时间之前由另一个线程设置。

最简单的惰性单例是使用枚举

public enum DefaultCache implements ICache {
     INSTANCE
}

我假设您没有这样做,因此您可以更改实现。

顺便说一句:我建议您仅在可能的情况下使用无状态单例,并尽可能对所有有状态对象使用依赖注入。

于 2012-12-19T15:32:11.257 回答
4

我会避免使用延迟初始化的单例进行争用检查:

public class Singleton {
    public static ICache getInstance() {
       return LazyCacheInitializer.INSTANCE;
    }

    private class LazyCacheInitializer {
       private static final ICache INSTANCE = new DefaultCache();
    }
}
于 2012-12-19T15:31:42.083 回答
1

是的,否则可以创建两个以上的实例。假设您有多个线程。第一个测试是在竞争条件下完成的,即一些线程同时将变量视为 null 并尝试设置实例。如果没有第二次检查,每个线程都会创建一个新实例。

于 2012-12-19T15:34:05.877 回答
0

假设cacheInstance是静态的,您应该考虑两种可能的初始化策略:

  • 延迟初始化:单例字段在首次使用时填充了一个实例(如您的示例中所示)。

  • (标准)初始化:一旦类被 JVM 加载,单例字段就会被一个实例填充。

所以如果你打算使用惰性初始化,你应该检查是否为空,否则你只会返回一个空指针。

或者,您也可以在静态块中初始化该字段。

//class declaration
 static {   
  cacheInstance = new DefaultCache(); 
 }

或在您声明它的同一行

private static final DefaultCache cacheInstance = new DefaultCache(); 
于 2012-12-19T15:31:06.803 回答
0

您始终可以使用enumPeter 发布的基于单例模式的实现,但请记住,您的所有实现都是每个类加载器的单例。

您还必须留意Cloneable界面。

于 2012-12-19T16:13:17.417 回答
-1

有一个私有构造函数来避免所有这些检查。

class Singleton {
    private static final Singleton instance = new Singleton();

    private Singleton() {
    }

    public static final Singleton getInstance() {
        return instance;
    }
}
于 2012-12-19T15:29:29.287 回答