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保证线程安全。

posted on 2014-11-04 14:22  暖 暖  阅读(207)  评论(0编辑  收藏  举报