1.前言
单例模式作为Java和Android中使用最广泛的设计模式,了解它对于我们设计出优秀的代码有很大的帮助。针对之前的文章进行更加详细的补充,也推荐大家前去学习《大话设计模式》和《Android源码解析和设计模式》这两本书。对于初学者学习设计模式很有帮助。
言归正传。
2.单例模式的定义
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
3.使用场合
不能自由构造对象的情况下(比如,某个类实例一次会消耗很多资源,如要访问IO和数据库资源,考虑使用单例模式)
4.单例模式特点
①构造方法(函数)被私有化(private);
②只能通过getInstance()方法取得Singleton(单例)类的实例化对象;
③不管外部如何操作,最终也只有一个实例化对象;
④在单例设计模式中一定会存在一个static方法,用于取得本类的实例化对象。
5.单例模式共有6种写法,它们各有利弊。
5.1 饿汉模式
public class Singleton{
private static Singleton instance = new Singleton();//1
private Singleton(){
}
public staitc Singleton getInstance(){
return instance;
}
}
类加载时就完成初始化(注释1),导致类加载慢,但获取对象速度快。该方式基于类加载机制,避免了多线程的同步问题。如果从始至终使用该实例,会造成内存浪费。
5.2 懒汉模式(线程不安全)
public class Singleton{
private static Singleton instance;//声明一个静态对象
private Singleton(){
}
public staitc Singleton getInstance(){
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
懒汉模式是声明一个静态对象,并在用户第一次调用getInstance时进行初始化,上面的饿汉模式是在声明静态对象时就已经初始化。懒汉模式虽然节约了资源,但第一次加载时需要实例化,反应稍慢。且该懒汉模式无法在多线程下正常工作。
5.3 懒汉单例模式(线程安全)
public class Singleton{
private static Singleton instance;//声明一个静态对象
private Singleton(){}
public static synchronized Singleton getInstance(){
if(instance == null){
instance = new Singleton();//第一次调用时初始化
}
return instance;
}
}
该懒汉模式比起上面的多了一个“synchronized”关键字,正是因为它该模式才可以在线程中很好的工作,但是每次调用getInstance方法时都需要进行同步。这又会造成不必要的同步开销,大部分情况下并不会使用同步,所以该懒汉模式也是不推荐使用的。
5.4 单例模式之DCL(双重检查)单例模式(推荐使用)
public class Singleton{
//在JDK是1.5或之后的版本,下面的①改为private volatile static Singleton mInstance = null;
private static Singleton mInstance = null;//①
private Singleton(){
}
public static Singleton getInstance(){
//第一次判断为了避免不必要的同步
if(mInstance == null){
synchronized(Singleton.class){
if(mInstance == null){
//为了在null的情况下创建实例
mInstance = new Singleton();
}
}
}
return mInstance;
}
}
getInstance方法对mInstance进行了两次判空;第一次是为了不必要的同步,第二次是在Single等于null的情况下才去创建实例。使用volatile会影响性能,但考虑到程序正确性,牺牲的那点性能是值得的。
DCL的优点是资源利用率高。缺点是第一次加载时反应稍微慢一点,高并发环境下有一定的缺陷。但DCL也有着自己的问题,它在某些情况下会出现失效,称为DCL失效。一般这个时候会建议使用静态内部类单例模式来代替DCL模式。
5.5 静态内部类单例模式
//静态内部类单例模式
public class Singleton{
private Singleton(){}
public static Singleton getInstance(){
return SingletonHolder.sInstance;
}
/**
* 静态内部类
*/
private static class SingletonHolder{
private static final Singleton sInstance = new Singleton();
}
}
第一次加载Singleton类的时候不会初始化sInstance,只有第一次调用getInstance方法时虚拟机加载SingleHolder并初始化sInstance。这样既可以保证线程安全,又可以保证Singleton类的唯一性。该模式是推荐使用的单例模式。
5.6 枚举单例模式
public enum Singleton{
INSTANCE;
public void doSomething(){
}
}
枚举单例在实际开发中很少会被使用,这里也不会过多介绍。
最后,还是那句老话:合适的才是最好的。选择模式前要仔细考虑下:是否为复杂的并发环境,是否需要控制单例对象的资源消耗等。考虑清楚再写。