公用技术——设计模式1——创建型模式——单例模式

1、概念

  单例模式是为了保证在一个JVM中类的实例只存在一个。这样做的目的是:

  1. 某些对象的生命周期很短,每次都创建会造成内存的浪费,例如Integer。
  2. 某些对象,存在第二个实例时属于一种逻辑错误,例如Spring的applicationContext,Servlet的ServletContext等等。
  3. 某些对象由于它的职责比较单一,存在第二个没有任何意义,例如工厂类。

  它的解决方案是:

  • 不允许创建其他对象,所以必须是私有的构造器
  • 必须存在一个public方法,它的返回值是当前对象的实例。

2、UML图

3、代码

3.1 懒汉式

普通场景下有两种实现方案,第一种实现方案称为懒汉式。

public Class SingletonExample{
	private static SingletonExample example;
	// 私有的构造器
	private SingletonExample()
	{
	}
	
	/**
	 * 
	 * @Title: getInstance  
	 * @Description: 公共的访问实例对象的方法,必须使用static   
	 * @return 实例对象
	 */
	public static SingletonExample getInstance()
	{
		if(example == null)
		{
			return new SingletonExample();
		}
		return example;
	}
}

3.2 恶汉式

public class SingletonExample {
	private static SingletonExample example = new SingletonExample();
	// 私有的构造器
	private SingletonExample(){
	}
	
	/**
	 * 
	 * @Title: getInstance  
	 * @Description: 公共的访问实例的方法 
	 * @return 实例对象
	 */
	public static SingletonExample getInstance(){
		return example;
	}
}

  两种方式比较:懒汉式在定义成员变量时没有进行初始化,在getInstance方法中添加了非NULL判断,而恶汉式在定义成员变量时直接初始化。在getInstance方法中直接返回实例对象。

4、讨论

  问题1:如何确保多线程环境下只有一个实例?

  答:为了保证在多线程情况下只有一个实例对象,需要使用synchronize关键字进行加锁,第一种实现方式是在getInstance方法上加锁,另外一种实现方式是在Class文件上加锁。在getInstance方法中如果包含其他逻辑代码,会导致整个程序非常缓慢,所以第二种方案拥有更好的性能。

  第一种方式,在方法上添加锁。

public static synchronized SingletonExample getInstance(){}

  第二种方式,在Class文件上添加锁

public static SingletonExample getInstance()
{
	if(example == null)
	{
		synchronized(SingletonExample.class)
		{
			return new SingletonExample();
		}
	}
	return example;
}

    问题2:如何保证多个类加载器,多个JVM下只有一个实例?

    答:当拥有多个类加载器时,每个类加载器拥有该对象的一个实例,不存在多个类加载器拥有同一个实例对象的情形。       多个JVM下必定存在不同的实例。

    问题3: 懒汉式与恶汉式有什么区别?

  答:从代码中可以看到恶汉式提前创建了对象,如果创建对象的过程很耗性能或者是创建对象需要一些耗时的操作,此时建议使用赖汉式,即需要该对象时在创建。如果对象本身不那么复杂,我们日常启动tomcat时会加载成千上万个类,多创建几个对象也没有多大影响,例如加载工厂类,创建Integer这类型的对象,此时建议使用恶汉式,提前加载。

  衡量懒汉与恶汉的区别,以及是否使用sync关键字,都是考虑到创建对象是否会影响性能,如果确定不影响,建议使用恶汉式。会影响,建议使用懒汉式。

5、示例

  1. 第一类问题,对象生命周期短,反复创建会浪费内存的情形,可以参考Integer类。
  2. 第二类问题,存在多个实例时是一种逻辑错误,可以参考在一个request请求线程中,出现相同Servlet的多个实例。
  3. 第三类问题,多个实例没有任何意义,对象的职责比较单一,这类型很常见,例如工厂类。
原文地址:https://www.cnblogs.com/rain144576/p/12330105.html