什么是单例模式
所谓单例模式,就是确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例的设计模式。单例模式是最简单的设计模式,也是应用最广的设计模式。一般用于避免产生多个对象消耗过多的资源或者某种类型的对象必须独一无二的情景。
单例模式的实现方式
(1)饿汉式
单例模式极其简单,仅有一个单例类,并且每个单例都必须有一个无参的构造方法。既然常用于确保某种类型的对象必须独一无二的情景,代码如下:
public class Emperor {
private static final Emperor emperor = new Emperor();
private Emperor(){}
public static Emperor getEmperor(){
return emperor;
}
}
从上述代码中可以看到,类不能通过new的形式构造对象,只能用方法来获取唯一的静态对象。这种在声明的时候就初始化的实现方式就叫做饿汉式。
(2)懒汉式
与饿汉式不同,懒汉式只有在第一次调用方式时才进行初始化。实现代码如下:
public class Singleton { private static Singleton instance; private Singleton(){} public static synchronized Singleton getInstance(){
if (instance == null){ instance = new Singleton(); } return instance; } }
懒汉式在方法中添加了synchronized关键字,可以在多线程情况下确保单例对象独一无二。但即使已经被初始化,每次调用还会进行同步,会消耗不必要的资源,并且第一次加载时进行实例化会拖慢反应速度,因此懒汉式一般不建议使用。但懒汉式并非一无是处,如果一直没有人用的话,就不会创建实例,则是节约空间。这是以时间换空间的实现方式,与饿汉式的以空间换时间各有所长。
注意上面代码的getIntance()方法,实战中见到getIntance()就是单例模式的准确率八九不离十。
(3)DCL式
Double Check Lock(以下简称DLC)实现单例模式既能够在需要时才初始化对象,又能保证线程安全。代码如下:
public class Singleton {
private static Singleton instance;
private Singleton(){}
public static synchronized Singleton getInstance(){
if (instance == null){
synchronized (Singleton.class){
if (instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
DCL可以保证无论何时读取这个变量,都是读到内存中最新的值,无论何时写这个变量,都可以立即写到内存中。DCL是目前单例模式最常见的实现方式。
(4)静态内部类式
DCL尽管能完美解决资源消耗、同步多余、线程不安全的问题,却有低概率在并发场景比较复杂的情况下失效(少见于J2EE和Hadoop等场景,绝少见于Android场景)。因此在对性能要求极高的情况下我们可以采取静态内部类式来实现单例模式。代码如下:
public class Singleton {
private Singleton(){}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
private static class SingletonHolder{
private static final Singleton instance = new Singleton();
}
}
静态内部类式利用ClassLoader机制来保证初始化时仅有一个线程,不但不会造成性能损耗,还是天衣无缝的安全方式。
(5)单例模式的容器式管理
一个软件也可能有多个单例对象。我们先建立一个管理类:
public class InstanceManager {
public static final String WEI = "a";
public static final String SHU = "b";
public static final String WU = "c";
private static Map<String,Object> emperors = new HashMap<>();
private EmperorManager(){}
public static void ascendEmperor(String key,Object emperor){
if (!emperors.containsKey(key)){
emperors.put(key,emperor);
}
}
public static Object getEmperor(String key){
return emperors.get(key);
}
}
然后就可以管理多个单例对象了:
InstanceManager.ascendEmperor(EmperorManager.WEI,WeiEmperor.getEmperor());
InstanceManager.ascendEmperor(EmperorManager.SHU,ShuEmperor.getEmperor());