单例模式(延迟加载)的实现方法
单例模式分为非延迟加载(饿汉式)和延迟加载(懒汉式),延迟加载可以有效提高系统资源的利用效率,所以通常使用延迟加载来实现。
一、通过双重检查锁实现
对于延迟加载,如果在整个返回单例对象的方法上加同步锁(synchronized),系统开销较大,因此可以通过双重检查锁(DCL)来实现。
1 public class Singleton { 2 3 // 私有静态实例,防止被引用 4 private static volatile Singleton instance = null; 5 6 // 私有构造方法,防止被实例化 7 private Singleton() { 8 } 9 10 // 双重检查锁(DCL)获取实例 11 public static Singleton getInstance() { 12 if (instance == null) { 13 synchronized (Singleton.class) { 14 if (instance == null) { 15 // volatile 保证了此行代码不会被重排序,从而保证可以返回一个完整初始化的对象 16 // 即是,执行语句3时,之前的所有操作已经完成(语句3不会被重排序到语句2之前) 17 // 1. memory = allocate(); // 内存分配 18 // 2. ctorSingleton(memory); // 初始化 19 // 3. instance = memory; // 返回对象引用 20 instance = new Singleton(); 21 } 22 } 23 } 24 return instance; 25 } 26 27 }
注意这里的volatile,保证了多线程环境下一个线程不会获取到另一个线程返回的还未完成初始化的对象。
二、通过静态内部类实现
利用JVM本身关于静态内部类的特性,可以实现单例对象的延迟且线程安全的创建。
1 public class Singleton2 { 2 3 /** 4 * 私有构造方法,防止被实例化 5 */ 6 private Singleton2() { 7 } 8 9 /** 10 * 使用静态内部类维护单例对象,该内部类的实例与外部类的实例没有绑定关系 11 * 而且第一次被调用时(getInstance方法)才会加载,从而实现了延迟加载 12 */ 13 private static class SingletonHolder { 14 // JVM保证了静态内部类被加载的时候是线程安全的,且只被实例化一次 15 // 这里的私有没有什么意义 16 /* private */ static Singleton2 instance = new Singleton2(); 17 } 18 19 /** 20 * 获取实例 21 */ 22 public static Singleton2 getInstance() { 23 // 外围类能直接访问内部类(不管是否是静态的)的私有变量 24 return SingletonHolder.instance; 25 } 26 27 }