1.定义
确保一个类只有一个 实例,自己实例化并向整个系统提供这个实例。
关键点:
a.构造函数不对外开放,一般为Private;
b.通过一个静态方法或者枚举返回单例类对象;
c.确保单例类对象有且只有一个,尤其是多线程环境下;
d.确保单例类对象反序列化不会重新构建对象。
2.使用场景
避免产生多个对象消耗过多资源,如要访问IO和数据库资源,或某种类型对象有且只有一个。
3.实现方式
3.1 锇汉模式 和 懒汉模式
//饿汉模式
public class Singleton {
private final static Singleton instance =new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
//懒汉模式
public class Singleton {
private static Singleton instance ;
private Singleton(){}
public static synchronized Singleton getInstance(){
if (instance == null){
instance = new Singleton();
}
return instance;
}
}
声明一个静态对象,饿汉模式在声明对象时就已经初始化,懒汉模式是用户在第一次调用getInstance方法时进行初始化。
优点:懒汉单例只有在使用时才会初始化
缺点:每次调用都需要进行同步,造成不必要的开销
3.2 Double Check Lock (DCL)
//DCL
public class Singleton {
private volatile static Singleton instance;
private Singleton(){}
public synchronized static Singleton getInstance(){
if (instance == null){
synchronized (Singleton.class) {
if (instance == null)
instance = new Singleton();
}
}
return instance;
}
}
DCL方式实现,既能够在需要是初始化,又能保证线程安全,且单例对象初始化后调用getInstance不进行同步锁。
第一层判断空:避免不必要的同步
第二层判断空:在null的情况下创建实例
添加volatile关键字保证instance对象每次都从内存读取,保证处理器顺序执行,保证程序的正确性。
缺点 在高并发的场景由于java内存模型的原因偶尔会有概率性失败
3.3 静态内部类实现单例
//静态内部类方式
public class Singleton {
private Singleton() {}
public static Singleton getInstance(){
return SingletonHolder.intance;
}
private static class SingletonHolder{
private static final Singleton intance = new Singleton();
}
}
第一次加载Singleton类时不会初始化,只有在第一次调用getInstance方法才会导致instance被初始化。因此,第一次调用getInstance方法会导致虚拟机加载SingletonHodler类,不仅能保证线程安全,也能保证唯一性,同时也延迟了实例化加载。
3.4 枚举方式
//枚举单例模式
public enum SingletonEnum {
INSTANCE;
public void function(){
System.out.print("to do ..");
}
}
写法最简单,枚举在Java中与普通类是一样的,不仅能有字段,还能有自己的方法,枚举类默认创建是线程安全的,且任何情况下他都是一个单例。上述其他实现单例模式中在反序列化时候可以通过特殊途径创建一个新的实例,相当于调用该类的构造函数。上述实现模式要杜绝单例对象在反序列化的时候重新生成对象,则必须加入readResolve函数,在readResolve方法中将单例对象返回,而不是重新生成一个对象(如下)。
//单例模式 反正反序列时通过特殊途径创建新实例
public class Singleton implements Serializable {
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;
}
//加入readResolve方法 将单例对象返回 而不是重新生成新的对象
public Object readResolve() throws ObjectStreamException{
return instance;
}
}
而对于枚举则不存在这个问题,即使反序列话也不会重新生成实例。
3.5 容器实现单例模式
//容器实现单例
public class Singleton {
private static Map<String,Object> objMap = new HashMap<String, Object>();
private Singleton(){}
public static void regiseterService(String key,Object instance){
if (!objMap.containsKey(key)){
objMap.put(key,instance);
}
}
public static Object getService(String key){
return objMap.get(key);
}
}
这种方式便于管理多种类型的单例,对用户隐藏了实现,降低了耦合。
4.小结
优点:
1.单例模式在内存中只有一个实例,减少了内存开支,特别是需要频繁创建、销毁时。
2.单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置文件、产生其他依赖对象的时候,则可以应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
3.可以避免对资源的多重占用,如文件读写操作。
4.可以在系统设置全局访问点,优化共享资源访问。
缺点:
1.一般没有接口,拓展不方便。
2.单例如果持有Context,很容易引发内存泄漏,要注意传递给单例对象的context最好是Application Context。
-------------------------------------------------------------------------------------------------------------------------------------------------