1.饿汉式:
public class Singleton{
// 在自己内部定义一个自己的实例
// 注意,这个是private 只供内部调用
private static Singleton instance = new Singleton();
//将构造函数设为私有
private Singleton(){
}
// 静态工厂方法,提供一个供外部使用得到对象的静态方法
public static Singleton getInstance(){
return instance;
}
}
2. 懒汉式:
public class Singleton{
private static Singleton instance = null;
private Singleton(){
}
public static synchronized Singleton getInstance (){
if(instance==null)
instance = new Singleton();
return instance;
}
}
使用 Java 语法的双重检验锁定 Singleton 代码
// C++ port to Java class Singleton { public static Singleton Instance() { if (_instance == null) { synchronized (Class.forName("Singleton")) { if (_instance == null) { _instance = new Singleton(); } } } return _instance; } protected Singleton() { } private static Singleton _instance = null; } |
在使用 Java 语法的双重检验锁定 Singleton 代码示例中,我们直接将 C++ 代码移植到 Java 代码,以便利用 Java 关键部分块(已同步)。 主要差别是不再有单独的声明和实现部分,没有指针数据类型,并且采用了新的双重检验机制。 双重检验发生在第一个 IF 块上。 如果成员变量为空,则执行进入关键部分块,该块再次双重检验该成员变量。 仅在通过此最终测试后,才会实例化该成员变量。 一般来说,两个线程无法使用这种方法创建两个类实例。 另外,因为在第一次检查时没有出现线程阻塞,所以对此方法的大多数调用不会由于必须进入锁定而导致性能下降。 目前,在实现 Singleton 模式时,很多 Java 应用程序中都广泛使用这种方法。 这种方法很巧妙,但也有瑕疵。 某些优化编译器可以将惰性初始化代码优化掉或对其重新进行排序,并且会重新产生线程安全问题。 有关更深入的解释,请参阅 "The Double-Check Locking is Broken" Declaration。
另一种试图解决此问题的方法可能是,在成员变量声明中使用 volatile 关键字。 这应该告诉编译器不要对代码重新排序,并且放弃优化。 目前,这是唯一建议的 JVM 内存模型,并且不会立即解决该问题。
实现 Singleton 的最好方法是什么? 最终(而不是碰巧),Microsoft .NET 框架解决了所有这些问题,从而更易于实现 Singleton,却不会产生我们目前讨论的不利副作用。 .NET 框架以及 C# 语言允许我们在必要时通过替换语言关键字,将上述的 Java 语法移植到 C# 语法。 因此,Singleton 代码变为以下内容:
以 C# 编码的双重检验锁定
// Port to C# class Singleton { public static Singleton Instance() { if (_instance == null) { lock (typeof(Singleton)) { if (_instance == null) { _instance = new Singleton(); } } } return _instance; } protected Singleton() { } private static volatile Singleton _instance = null; } |