简单且广泛运用的模式——单例模式

来源:互联网 发布:linux vim 不保存退出 编辑:程序博客网 时间:2024/06/01 19:29

单例模式基本介绍

单例模式算得上是应用最广泛的模式之一。单例模式的类要保证只能有一个实例存在,很多时候有些消耗资源的类在整个系统中只需要创建一个实例就够用了,多余的实例则会增加系统的开销。这是基本的使用场景。

单例模式UML图

class singleton
Client–高层客户端;
Singleton-单例类。

单例模式关键点:

1) 一般构造函数私有化,使用Private修饰,不对外开放;
2) 通过一个静态方法(例如getInstance())或者枚举来返回单例类对象;
3) 确保单例类的对象有且只有一个,尤其是在多线程的环境下;
4) 确保单例类对象在反序列化时不会被重新构建对象。

单例模式的几种实现方法

1)饿汉模式

public class Singleton{    //在声明变量的时候直接实例化    private static final Singleton instance = new Singleton();    //构造函数私有化    private Singleton(){}}

2)懒汉模式

public class Singleton{    private static Singleton instance;    private Singleton(){};    //在第一次调用此方法时实例化单例对象,之后返回第一次调用创建的对象    public static synchronized Singleton getInstance(){        if(instance == null){            instance = new Singleton();        }        return instance;    }}

这里使用了synchronize来修饰getInstance方法,就是为了在多线程环境下,保证单例类只有一个实例化对象。不过也是因为被synchronize修饰了,所以每次调用getInstance方法时,都会进行同步操作,这样就造成了不必要的同步开销,浪费了资源,这也是懒汉模式存在的问题。

3)Double Check Lock(DCL)方式

DCL实现单例的优点在于是其在需要时才初始化单例,这样既能够保阵线程的安全,又不会形成不必要的同步开销。

public class Singleton{    private static Singleton mInstance = null;    private Singleton(){}    public static Singleton getInstance(){        if(mInstance == null){            synchronized(Singleton.class){                if(mInstance == null){                    mInstance = new Singleton();                }            }        }        return mInstance;    }}

这里的getInstance方法进行了两次是否为空的判断,第一次是为了避免造成不必要的同步开销,第二次是为了在instance为null的时候才创建实例。不过这种方法在某些情况下会出现失效的问题,这个问题呗成为双重检查锁定失效。不过一般除非使用环境低于JDK1.6(Java编译器允许处理器乱序执行,JDK1.5之前的JMM中,部分操作顺序无法保证),或者并发场景十分复杂,其余场景这种方式一般能够满足需求。

4)静态内部类单例模式

public class Singleton{    private Singleton(){}    public static Singleton getInstance(){        return SingletonHolder.mInstance;    }    private static class SingletonHolder{        private static final Singleton mInstance = new Singleton():    }}

这种方法在第一次加载Singleton类时不会初始化mInstance,只有在调用getInstance方法才被初始化,这种方法既可以保证线程安全,也能保证单例对象的唯一性,同时延迟了单例的实例化,是比较推荐的一种单例模式实现方式。

5)枚举单例

public enum SingletonEnum{    INSTANCE;    public void doSth(){}}

在Java中,枚举和普通的类时一样的,不仅能够有字段,还能有自己的方法,而且枚举实例的创建时线程安全的。

6)使用容器的单例模式

public class SingletonManager{    private static Map<String,Object> objMap = new HashMap<String,Object>();    private SingletonManager(){}    //将单例对象添加到管理容器中    public static void registerService(String key,Object instance){        if(!objMap.containsKey(key)){            objMap.put(key,instance);        }    }    //通过key来获取需要的单例对象    public static Object getService(String key){        return objMap.get(key);    }}

这种方式在程序初始的时候,将多重单例类型注入到一个统一的单例管理容器中,在使用的时候根据key来获取对应类型的单例对象,这种方式对方便了对单例对象的管理,也降低了用户成本,单例的具体实现对用户隐藏,降低了耦合度。

单例模式的优缺点

优点
1)由于单例模式只会在整个应用中生成一个实例,相比那些频繁需要创建、销毁的对象,有更少的内存开始和系统的性能开销。
2)单例可以避免对资源的多重占用。可以避免对一个文件进行同时的写操作。
3)单例模式可以在系统设置全局访问点,优化和共享资源访问。
缺点
1)单例模式一般没有接口,扩展很困难,基本只能通过修改代码了。
2)在android中,如果单例对象持有Context,那么很容易就引发内存泄漏,对此的建议是尽量将Application Context传递给单例对象使用。

关于反序列化

在java中,即使构造函数被私有化了,但是在反序列化的时候,还是回去创建类的一个新的实例,反序列化操作提供了一个很特别的钩子函数–readResolve()方法,此方法可以让开发人员控制对象的反序列化。

public final class Singleton implements Serializable{    private static final long serialVersionUID = 0L;    private static final Singleton INSTANCE = new Singleton();    private Singleton(){}    public static Singleton getInstance(){        return INSTANCE;    }    private Object readResolve() throws ObjectStreamException{        return INSTANCE;    }}
原创粉丝点击