一、概述
今天学习SpringMVC的时候,注意到Controller是一个单例模式,虽然是最简单的设计模式,但是发现自己对其并没有太多了解,于是准备学习下。
二、单例模式的五种形式分析
注意下,这里提到的5种形式并不是说单例模式只有这5种模式,而是我通过查找资料总结出来的。
1、饿汉式(不推荐)
代码如下:
/**
* 饿汉式
*/
class SingletonDemo1 implements Serializable {
private static SingletonDemo1 instance = new SingletonDemo1() ;
private SingletonDemo1(){}
public static SingletonDemo1 getInstance(){
return instance ;
}
}
这种模式的特点是在申明静态类对象的时候实例就已经创建出来了。
- 优点:可以每次很快的获取实例,速度较快。
- 缺点:优点浪费内存资源,有时候我们需要在需要的时候创建。
2、懒汉式(不推荐)
/**
* 懒汉式
*/
class SingletonDemo2{
private static SingletonDemo2 instance ;
private SingletonDemo2(){}
public static synchronized SingletonDemo2 getInstance(){
if( instance == null ){
instance = new SingletonDemo2() ;
}
return instance ;
}
}
这种模式的特点是在需要实例的时候创建。主要在getInstance()方法加了锁,主要防止多线程并发问题。
- 优点:由于它是在需要的时候才创建,和第一种模式的最大区别就是节省了资源。
- 缺点:它的缺点也是比较明显的,第一次获取实例时会比较慢,因为需要初始化,最大的问题是每次获取都需要同步,增加了额外的开销。
3、两次检查(推荐)
这个模式的具体叫什么名字我也不知道。。。开代码吧
class SingletonDemo3{
private static volatile SingletonDemo3 instance ;
private SingletonDemo3(){}
public static SingletonDemo3 getInstance(){
if( instance == null ){
synchronized (SingletonDemo3.class) {
if( instance == null ){
instance = new SingletonDemo3() ;
}
}
}
return instance ;
}
}
从代码看,和第二种有点类似,但是好像又多了点什么。它对instance做了两次判断,第一次判断是解决不必要的同步,第二次则是在instance为null的条件下才创建。
- 优点:解决了第二种最大的同步问题,资源利用率高。
- 缺点:在第一次获取instance较慢。
4、静态内部类模式(推荐)
这种模式我以前也没见到过,查资料的。
class SingletonDemo4{
private static SingletonDemo4 instance ;
private SingletonDemo4(){}
public static SingletonDemo4 getInstance(){
return SingletonHolder.instance ;
}
static class SingletonHolder{
public static SingletonDemo4 instance = new SingletonDemo4() ;
}
}
- 优点:第一次加载类时不会读instance实例化,只有在调用getInstance()才会实例化,而且它是线程安全的。(至于为什么是线程安全的,我们知道导致线程不安全的前提肯定不会只有一行代码)
- 缺点:这种模式好像并没有太明显的缺点。
5、容器模式
class SingletonDemo5{
private static Map map = new HashMap();
private SingletonDemo5(){}
public static void registerService(String key,Object instance){
if(!map.containsKey(key)){
map.put(key, instance);
}
}
public static Object getInstance(String key){
return map.get(key);
}
}
这种模式的特点是通过一个Map管理多种单例,
- 优点:可以帮助实现解耦
三、细节
单例模式是不是在任何情况下都不会重新创建呢?答案是否定的,因为我们在序列化将Instance写入本地文件,然后再将其读出,两者是不一样的。
//将SingletonDemo1对象写入序列化
SingletonDemo1 instance = SingletonDemo1.getInstance();
System.out.println("序列化之前:" + instance );
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("a.txt")));
oos.writeObject(instance);
oos.close() ;
//将SingletonDemo2对象反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("a.txt"))) ;
SingletonDemo1 instance2 = (SingletonDemo1) ois.readObject();
System.out.println("反序列化之后:" + instance2 );
程序输出结果
序列化之前:com.lw.singleton.SingletonDemo1@659e0bfd
反序列化之后:com.lw.singleton.SingletonDemo1@5c647e05
从结果可以看出,在反序列化的过程中调用了SingletonDemo1对象的私有构造函数
四、总结
不管以哪种单例模式,其核心思想是一样的,首先将其构造函数私有化,接着通过静态方法获取实例,然后就是性能的问题了,具体选择哪一种看具体的情况。
OK,这篇就简单学习了下单例模式