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