设计模式复习篇——单例模式

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sliverbullets/article/details/83481256

单例模式的的使用场景:创建比较耗资源的、全局调用的类。

单例特点:
1.构造方法私有化。
2.有一个静态的方法用来获取对象或者一个静态对象。
3.线程安全,确保单例类对象有且只有一个,尤其在多线程环境下。
4.确保反序列化时不会重构对象

六种单例模式的实现:
1.饿汉模式

public class Singleton{ 
 	private static Singleton instance = new Singleton(); 
 	private Singleton(){}
} 

优点:类一加载,类的对象就开始初始化,减少了重复对象创建销毁
缺点:同样,这样会造成一定的浪费,并且线程不安全。
名字由来:不管你用不用一加载类就实例化对象。

2.懒汉模式

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

优点:对象在第一次调用时进行初始化,有一定程度的节约内存,线程安全。
缺点:第一次加载需要实例化,反应稍慢;synchronize这个同步操作导致每次调用这个getInstance方法的时候会消耗一部分资源。
名字由来:在你需要调用的时候才开始实例化对象。

3.Double Check Look(DCL)双重锁检查

 public class Singleton{ 
 	private static Singleton instance = null; 
 	private Singleton(){}
  	public static Singleton getInstance(){
   		 if(instance == null){                    //第一次检查,作用减少synchonsized使用   
	  		 (synchronsized.class){
		    		if(instance == null){             //第二次检查,保证单例
		    			 instance = new Singleton();
 		   		}
		   	}
	  	}
	   return Instance;
	 }
} 

优点:它相比懒汉模式不仅线程安全,并且它多了一次判断,如果不是第一次创建就不走同步代码,这样减少了性能消耗。
缺点:在JDK1.5由于java处理器采用乱序执行," Instance = new Singleton; "这句并不是原子语句(不可拆分语句就是原子语句),它分三个步骤①给对象分配内存空间②初始化对象③对象指向给分配的内存空间地址。它的②③语句在JDK5是不分顺序的,所以有可能我们的进程执行了①③后被中断执行其他地方了,然后用到这个对象,但此时对象虽然不为null但是并没用进行初始化,所以这也是不安全的。(JDK6对这个进行了改正,具体化了volatile,使用public static volatile Singleton Instance = null解决了这个问题,它保证了每次从内存中获取这个对象)。

4.静态内部类

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

优点:getInstance()在第一次调用时初始化对象,解决了DCL失效问题(上面的JDK5DCL存在的问题)。

5.利用容器实现

 public class SingletonManger{
 	private static HashMap<String, Object> objMap = new HashMap<String, Object>(); 
 	private SingletonManger(){}
	 public static registerService(String key, Object instance){
	 	 if(!objMap.containKey(key)){
  	 		objMap.put(key ,instance);
		  }
 	}
 	public static Object getService(String key){
 		 return objMap.get(key);
 	}
}

优点:可以管理多种单例,并且隐藏了具体实现,降低了耦合度。

6.枚举

 public enum SinglonEnum{
	 INSTANCE;
 	public void doSomething(){
		  System.out.plintln("do thing");
	 }
}

优点:写法简单,并且默认枚举实例的创建是线程安全的,并且任何情况下他都是一个单例,并且它很巧妙地避免了反射攻击。

在上述五种方法(除了枚举)中需要注意这一点,防止对象多次创建对象,要杜绝单例对象在反序列化时重新生成对象必须加入如下方法:
private Object readResolve() throws ObjectStreamException{
return sInstence;
}
因为序列化可以将一个单例的实例对象写到磁盘,然后再读回来,(前面经过了一个读写操作然后就不安全了!)虽然单例模式构造器私有了,但反序列化会通过特殊途径相去创建这个类的一个新的实例,相当于调用该类的构造器。上面这个方法可以使开发人员控制对象的反序列化,将这个方法写成上面那样就可以直接返回这个对象,而不是重新生成一个新的对象。
但枚举不存在这个问题,因为即使反序列化它也不会生成新的对象。
关于枚举和反序列化问题我觉得这个文章讲的十分清楚了:为什么要用枚举实现单例模式(避免反射、序列化问题)

总结:
优点:
1.由于单例模式在内存中只存在一个实例,减少了开支,特别是一个对象频繁的创建销毁,而且创建销毁时性能无法优化,单例模式的优点就十分明显。
2.由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的乘胜需要比较多的资源的时,如读取配置、产生其他依赖对象时则可以通过应用启动时直接生产一个单例对象,然后用永久驻留内存的方式来解决。
3.单例模式可以避免对资源的多重占用,例如一个写文件操作,由于只有一个实力存在,避免对同一个资源文件的同时写操作。
4.单例模式可以在系统设置全全局的访问点,优化和共享资源访问,例如,可以设计一个单例类,负责所有数据表的映射处理。

缺点:
1.单例模式一般没有接口,扩展很困难,若要扩展,除了修改代码基本无其他办法。
2.如果你的单例对象持有Activity的context引用需要注意内存泄漏,最好使用ApplicationContext。

猜你喜欢

转载自blog.csdn.net/sliverbullets/article/details/83481256
今日推荐