admin 发表于 2016-6-19 14:10

你真的了解单例模式吗?

       本文介绍了为何需要单例模式,单例模式的设计要点,饿汉和懒汉的区别,并通过实例介绍了实现单例模式的九种实现方式及其优缺点。

为何需要单例模式


对于系统中的某些类来说,只有一个实例很重要,例如,一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。


单例模式设计要点


保证该类只有一个实例。将该类的构造方法定义为私有方法,这样其他处的代码就无法通过调用该类的构造方法来实例化该类的对象
提供一个该实例的访问点。一般由该类自己负责创建实例,并提供一个静态方法作为该实例的访问点


饿汉 vs. 懒汉


饿汉 声明实例引用时即实例化
懒汉 静态方法第一次被调用前不实例化,也即懒加载。对于创建实例代价大,且不定会使用时,使用懒加载模式可以减少开销


实现单例模式的九种方法



1. 线程不安全的懒汉 - 多线程不可用
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {};
public static Singleton getInstance() {
    if (INSTANCE == null) {
      INSTANCE = new Singleton();
    }
    return INSTANCE;
}
}
优点:达到了Lazy Loading的效果
缺点:只有在单线程下能保证只有一个实例,多线程下有创建多个实例的风险


2. 同步方法下的懒汉 - 可用,不推荐
public class Singleton {
private static Singleton INSTANCE;
private Singleton() {};
public static synchronized Singleton getInstance() {
    if (INSTANCE == null) {
      INSTANCE = new Singleton();
    }
    return INSTANCE;
}
}优点:线程安全,可确保正常使用下(不考虑通过反射调用私有构造方法)只有一个实例
缺点:每次获取实例都需要申请锁,开销大,效率低


3. 同步代码块下的懒汉 - 不可用public class Singleton {
private static Singleton INSTANCE;
private Singleton() {};
public static Singleton getInstance() {
    if (INSTANCE == null) {
      synchronized (Singleton.class) {
      INSTANCE = new Singleton();
      }
    }
    return INSTANCE;
}
}

优点:不需要在每次调用时加锁,
缺点:虽然使用了`synchronized`,但本质上是线程不安全的。


4. 不正确双重检查(Double Check)下的懒汉 - 不推荐


public class Singleton {
private static Singleton INSTANCE;
private Singleton() {};
public static Singleton getInstance() {
    if (INSTANCE == null) {
      synchronized(Singleton.class){
      if(INSTANCE == null) {
          INSTANCE = new Singleton();
      }
      }
    }
    return INSTANCE;
}
}优点:使用了双重检查,很大程度上避免了线程不安全,同时也避免了不必要的锁开销
缺点:依然存在创建多个实例的可能。因为每个线程都有自己的一份拷贝,并不能保证实例化后将INSTANCE的引用拷回主内存,不能保证对其它线程立即可见,所以仍然有可能造成多个实例被创建

5. 正确双重检查(Double Check)下的懒汉 - 推荐
public class Singleton {
private static volatile Singleton INSTANCE;
private Singleton() {};
public static Singleton getInstance() {
    if (INSTANCE == null) {
      synchronized (Singleton.class) {
      if (INSTANCE == null) {
          INSTANCE = new Singleton();
      }
      }
    }
    return INSTANCE;
}
}优点:使用了双重检查,同时使用`volatile`修饰`INSTANCE`,避免由于多线性同步和可见性问题造成的多实例
缺点:NA

6. 静态常量 饿汉 - 推荐
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {};
public static Singleton getInstance() {
    return INSTANCE;
}
}
优点:实现简单,无线程同步问题
缺点:在类装载时完成实例化。若该实例一直未被使用,则会造成资源浪费

7. 静态代码块 饿汉 可用public class Singleton {
private static Singleton INSTANCE;
static{
    INSTANCE = new Singleton();
}
private Singleton() {};
public static Singleton getInstance() {
    return INSTANCE;
}
}优点:无线程同步问题
缺点:类装载时创建实例,无Lazy Loading。实例一直未被使用时,会浪费资源

8. 静态内部类 推荐public class Singleton {
private Singleton() {};
public static Singleton getInstance() {
    return InnerClass.INSTANCE;
}

private static class InnerClass {
    private static final Singleton INSTANCE = new Singleton();
}
}优点:无线程同步问题,实现了懒加载(Lazy Loading)。因为只有调用`getInstance`时才会装载内部类,才会创建实例
缺点:NA

9. 枚举 不推荐public enum Singleton {
INSTANCE;
public void whatSoEverMethod() {
}





优点:无线程同步问题,且能防止通过反射创建新的对象
缺点:使用的是枚举,而非类。同时单一实例的访问点也不是一般单例模式的静态方法





原文转自 Jason's Blog

风林火山 发表于 2016-10-30 13:57

不错,总结的非常全面,,根据 数量 来进行 划分,,除了 单例,我们还可以实现 双例 模式, 多例 模式, 没有数量限制的 new 模式,new 模式的 变体   工厂方法模式~~~

风林火山 发表于 2016-10-30 13:57

这样按照实例 数量 进行 划分的 模式 就全部 囊括其中了~~~

剩斗士 发表于 2016-11-5 08:45

学习了,最近面试遇到过单例模式的,再遇到就不担心了:victory:
页: [1]
查看完整版本: 你真的了解单例模式吗?