1.定义
确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
2.使用场景
例如:在一个应用中,应该只有一个ImageLoader实例,这个ImageLoader中既包含线程池、缓存系统、网路请求等,创建这个实例非常消耗系统资源,因此没有理由让它多次构建。这种不能自由构造对象的情况就是单例模式的使用场景。其他的例如:IO操作、数据库操作等。
3.关键点
1)将构造函数私有化(privat关键字),不对外开放
2)通过一个静态方法对外提供单利类对象
3)确保单例对象有且只有一个,尤其是在多线程的环境下
4)确保单利类对象在反序列化时不会重新去构建对象
4.实现
方式一:懒汉模式
/**
* 单例模式-懒汉模式
*/
public class SingleTon {
private static SingleTon INSTANCE;
private SingleTon() {
}
private static synchronized SingleTon getInstance() {
if (INSTANCE == null) {
INSTANCE = new SingleTon();
}
return INSTANCE;
}
}
优点:确保该实例只有在使用时才会被实例化,在一定程度上节约了系统资源,同时使用关键字synchronized保证了全局对象的唯一性。
缺点:第一次加载时需要及时进行实例化,反应稍慢,以后每次调用时都需要进行同步,造成不必要的同步开销。
方式二:Double Check Lock
/**
* 单例模式-Double Check Lock
*/
public class SingleTon {
private static SingleTon INSTANCE;
private SingleTon() {
}
private static SingleTon getInstance() {
if (INSTANCE == null) {
synchronized (SingleTon.class) {
if (INSTANCE == null) {
INSTANCE = new SingleTon();
}
}
}
return INSTANCE;
}
}
注:两次判空的意义:1是为了避除处第一次创建需要同步之外其他不必要的同步2是为了在null的情况下创建实例,保证全局实例的唯一性
优点:资源利用率高,第一次执行getInstance时单例对象才会被实例化,效率高。
缺点:第一次加载时,反应稍慢,由于Java的内存模型原因偶尔会失败,在高并发情况下也有一定缺陷
方式三:静态内部类单例模式
/**
* 单例模式-静态内部类单例模式
*/
public class SingleTon {
private SingleTon() {
}
private static SingleTon getInstance() {
return SingleTonHolder.INSTANCE;
}
public static class SingleTonHolder {
private static final SingleTon INSTANCE = new SingleTon();
}
}
注:当第一次加载SingleTon类的时候并不会实例化INSTANCE,只有第一次调用SingleTon的getInstance方法时才会导致INSTANCE实例化。因此,第一次调用getInstance方法会导致虚拟机加载SingleTonHolder类。
优点:即能够确保线程安全,又能够保证单例对象的唯一性,同时延迟了单例的实例化,所以这是推荐的单例模式实现方式。
方式四:容器注入方式
/**
* 单例模式-容器注入方式
*/
public class SingleTonManager {
private static Map<String,Object> singleTonMap = new HashMap<String,Object>();
private SingleTonManager(){}
public static void registerSingleTon(String key,Object instance){
if(!singleTonMap.containsKey(key)){
singleTonMap.put(key,instance);
}
}
public static Object getSingleTon(String key){
return singleTonMap.get(key);
}
}
注:在程序开始的时候,将多种单例类型注入到一个统一的管理类当中,在使用的时候根据key获取对象对应类型的对象。
优点:管理多种类型单例对象,需要时通过统一的接口进行获取操作,降低了用户使用成本,也对用户隐藏了具体的实现细节,降低了耦合度。
核心思想
单例模式的核心思想是将构造函数私有化,并且通过静态方法获取一个唯一的实例,在这个获取的过程中必须保证线程安全、防止反序列化重新生产实例对象等问题。
小结
单例模式时运用频率很高的设计模式,但是由于在客户端通常没有高并发的情况,因此,选择哪种实现方式并不会有太大的影响,即便如此,出于效率考虑,我们仍推荐使用Double Check Lock和静态内部类的方式实现。
优点:
1.由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁创建的时候,而且创建或销毁时性能又无法优化,因此使用单例模式就会有很大的优势
2.由于单例模式只生成了一个实例,所以减少了系统的性能开销,当一个对象的产生需要花费较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后用永久驻留内存的方式实现。
3.单例模式剋避免对资源的多重占用,例如一个写文件操作,由于只有一个实例存在在内存中,避免对同一个文件的同时写操作。
4.单例模式可以在系统设置全局的访问点,优化和共享资源访问。
缺点:
1.单例模式一般没有接口,扩展很困难,若要扩展,只有修改代码一种方式。
2.单例对象如果持有Context,很容易引起内存泄漏,此时需要注意传递给单例对象的Context最好是同单例对象生命周期同步的,例如ApplicatonContext。