首页 > 代码库 > 设计模式--单例模式

设计模式--单例模式

1、定义

     • 确保一个类只有一个实例,且自行实例化,并向整个系统提供这个实例,这个类称为单例类,同时提供一个唯一的访问方法。 

2、要点

     • 一个类只有一个实例

     • 必须自行创建实例

     • 必须自行向整个系统提供这个实例

3、何时选用单例模式

     • 系统只需要一个实例对象

     • 客户调用类的单个实例,只允许使用一个公共访问点

     • 单例模式可扩展为多例模式,即一个类可以有一个实例共存

4、本质

     • 控制实例的数量

5、单例模式实现方式

     • 饿汉式

package com.eager.test;/*** 饿汉式单例模式*/public class EagerSingleton {		//4:定义一个静态变量来存储创建好的类实例	//直接在这里创建类实例,只会创建一次	private static final EagerSingleton instance=new EagerSingleton();		//1:私有化构造方法,好在内部控制创建实例的数目	private EagerSingleton()	{	}		//2:定义一个方法来为客户端提供类实例	//3:这个方法需要定义成类方法,也就是要加static	//这个方法里面就不需要控制代码了	public static EagerSingleton getInstance()	{		return instance;	}}

  说明:

    1)static变量,在类加载的时候进行初始化

            2)多个static变量,共享一块内存区域

            3)饿汉式的一个缺点就是不管这个实例是否被使用,该实例都会被创建,会有一点点浪费内存

     • 普通懒汉式

package com.lazy.test;/*** 普通懒汉单例模式*/public class CommonLazySingleton {		private static CommonLazySingleton instance=null;		//私有的构造方法,以便在内部控制创建实例的个数	private CommonLazySingleton()	{	}		//synchronized	public static CommonLazySingleton getInstance()	{		//如果没有值,说明还没有创建过实例,那就创建一个		//并把这个实例设置给instance		if(instance==null)		{			instance=new CommonLazySingleton();		}				//如果有值则直接使用		return instance;	}}

  说明:普通懒汉式虽然利用延迟加载,解决了需要使用实例时才创建,但是这种方式是线程不安全的,假如A线程走到20行,但是还没有走到22行进行创建instance,此时,线程B也很快判断出instance==null,就会出现线程不安全的现象。

      • 双重检查加锁懒汉式

package com.lazy.test;/*** 双重检查锁定懒汉单例模式*/public class DoubleCheckLockingLazySingleton {	/**	* 对保存实例的变量添加volatile的修饰	*/	private volatile static DoubleCheckLockingLazySingleton instance = null;		private DoubleCheckLockingLazySingleton()	{	}		public static DoubleCheckLockingLazySingleton getInstance()	{		//先检查实例是否存在,如果不存在才进入下面的同步块		if(instance == null)		{			//同步块,线程安全的创建实例			synchronized(DoubleCheckLockingLazySingleton.class)			{				//再次检查实例是否存在,如果不存在才真的创建实例				if(instance == null)				{					instance = new DoubleCheckLockingLazySingleton();				}			}		}		return instance;	}}

  说明:

   1)双重检查加锁方式解决了延迟加载与线程安全问题,但是用到了Java中的关键字volatile,被volatile修饰的变量的值,不会被本地线程所缓存,所以对该变量的读取都是从共享内存中读取,从而保证多线程的安全性。

            2)Java1.4及以前版本中,很多JVM对于volatile关键字的实现有问题,会导致双重检查加锁的失败,因此双重检查加锁的机制只能用再Java5及以上版本

       

3)由于volatile关键字可能会哦ing比掉JVM中一些必要的代码优化,因此运行效率不高。所以没有特别的需要,不建议使用。所以双重检查加锁策略虽然可以解决问题,但是不建议大量采用。

      • IoDH懒汉式

package com.lazy.test;/*** IoDH懒汉单例模式*/public class IoDHLazySingleton {		private IoDHLazySingleton()	{	}		/**	* 静态内部类,该内部类的实例与外部类的实例没有绑定关系,而且只有被调用才会装载,实现延迟加载	* 	* 多个实例的static变量会共享同一块内存区域	*/	private static class HolderClass	{		private final static IoDHLazySingleton instance=new IoDHLazySingleton();	}		/**	* 提供给其他类获取唯一实例的方法	*/	public static IoDHLazySingleton getInstance()	{		return HolderClass.instance;	}}

  说明:利用静态内部类既实现了延迟加载,又实现了线程安全,因此推荐的使用方法之一即为此种方式。

      • 枚举实现的饿汉式       

package com.enum_test;/*** 使用枚举来实现单例模式的示例*/public enum SingletonEnum {	/**	* 定义一个枚举的元素,它就代表了SingletonEnum的一个实例	*/	INSTANCE;		/**	* 示意方法,单例可以有自己的操作	*/	public String print() {		System.out.println("Enum测试实例调用方法");        return "Enum单例模式测试";    }     // public static Singleton getInstance() {    // return INSTANCE;    // }}

  说明:

    1)Java的枚举型实质上是功能齐全的类,可以有自己的属性和方法

            2)Java的枚举型的基本思想是通过public static final 域为每个枚举常量导出实例对象的

            3)因此,用枚举实现单例模式会更加简洁、方便、并且提供了序列化的机制,由JVM保证线程安全。

设计模式--单例模式