前言
单例模式属于创建型模式,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
1. 懒汉式
public class SluggardSingleton {
private static SluggardSingleton sluggardSingleton;
private SluggardSingleton(){}
public static SluggardSingleton getInstance(){
if (sluggardSingleton == null){
return new SluggardSingleton();
}
return sluggardSingleton;
}
}
懒加载,用的时候会对实例进行检查,有则返回 ,没有则新建,避免内存浪费, 该方式非线程安全。
2. 懒汉式线程安全
public class SluggardSecuritySingleton {
private static SluggardSecuritySingleton sluggardSecuritySingleton;
private SluggardSecuritySingleton(){}
public static synchronized SluggardSecuritySingleton getInstance(){
if (sluggardSecuritySingleton == null){
return new SluggardSecuritySingleton();
}
return sluggardSecuritySingleton;
}
}
与第一种方式唯一的区别 是增加了synchronized关键字 ,保证了线程安全 ,对执行效率会有所影响
3. 饿汉式
public class HungrySingleton {
private static HungrySingleton hungrySingleton = new HungrySingleton();
private HungrySingleton(){}
public static HungrySingleton getInstance(){
return hungrySingleton;
}
}
类加载时就初始化,浪费内存,好处是保证了线程安全
4. 双检锁
public class DoubleCheckLockSingletion {
private static DoubleCheckLockSingletion doubleCheckLockSingletion;
private DoubleCheckLockSingletion(){}
public static DoubleCheckLockSingletion getInstance(){
if (doubleCheckLockSingletion == null){
synchronized (DoubleCheckLockSingletion.class){
if (doubleCheckLockSingletion == null){
return new DoubleCheckLockSingletion();
}
}
}
return doubleCheckLockSingletion;
}
}
外层实例的判空处理不用让线程每次都加锁,而只是在实例未被创建的时候再加锁处理。同时也能保证多线程的安全。
至于为何在内层再加一层判空处理 ,这里引用《大话设计模式》书中解释:
对于instance存在的情况,就直接返回,这没有问题。当instance为null并且同时有两个线程调用 GetInstance()方法时,它们将都可以通过第一重instance==null的判断。然后由于lock机制,这两个线程则只有一个进入,另一个在外排队等候,必须要其中的一个进入并出来后,另一个才能进入。而此时如果没有了第二重的instance是否为null的判断,则第一个线程创建了实例, 而第二个线程还是可以继续再创建新的实例,这就没有达到单例的目的。
5. 静态内部类
public class StaticSingletion {
private static class StaticSingletionHodel{
private static final StaticSingletion SINGLETION = new StaticSingletion();
}
private StaticSingletion(){}
public static StaticSingletion getInstance(){
return StaticSingletionHodel.SINGLETION;
}
}
这种方式能达到双检锁方式一样的功效,但实现更简单。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
它跟第三种方式不同的是:第三种方式当类加载就会实例化,而这种方式只有被调用时才会实例化。
6. 枚举
public enum EnumSingletion {
INSTANCE;
public void method() {
}
}
更简洁,自动支持序列化机制,绝对防止多次实例化,保证了线程安全。
总结
一般情况下,懒汉方式(线程安全和非线程安全)不建议采用,建议使用第 3 种饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用第 5 种方式。如果涉及到反序列化创建对象时,可以尝试使用第 6 种枚举方式。如果有其他特殊的需求,可以考虑使用第 4 种双检锁方式。