设计模式之单例模式

什么是单例模式?

单例模式,是一种常用的软件设计模式,属于对象创建模式的一种。在应用这个模式时,单例对象的类必须保证只有一个实例存在。

单例模式的使用场景?

  • 系统关键组件,需要保证系统中只有一个实例;
  • 类的初始化需要消耗较多的系统资源;

如何实现?

  • 私有的构造函数。杜绝外部通过构造方法显式创建类的实例,确保系统中仅有一个实例的存在;
  • 提供单例实例的全局访问点。一般为 Method namegetInstance() 的静态方法,返回结果为单例实例;
  • 获取单例实例时,首先判断虚拟机中实例是否已存在,如果有则返回,如果没有则创建;

常见写法及分析

单例模式虽然简单,实现方式却比孔乙己老先生的茴字写法还要多。以下:

饿汉模式

/**
 * 单例(饥饿模式):静态变量初始化保证线程安全
 */
public final class HungerSingleton {

    /**
     * 私有构造方法,保证不能通过 new 创建新的实例
     */
    private HungerSingleton(){}

    /**
     * 静态示例变量
     */
    private static final HungerSingleton INSTANCE = new HungerSingleton();

    /**
     * 获取类的单例实例
     *
     * @return
     */
    public static HungerSingleton getInstance(){
        return INSTANCE;
    }
}

饿汉模式是最常用的单例写法,JDK 中的 java.lang.Runtime 就是这样实现的,基于 classloader 机制,这种写法可以避免多线程的同步问题;不过 instance 在类装载的时候就实例化,容易产生垃圾对象,对于基础服务中的非核心模块,不建议使用饿汉模式,而应该使用懒加载的单例写法-“按需创建”。

Synchronized 版(懒汉式)

/**
 * 同步版本的单例
 * 1 懒加载
 * 2 使用 Synchronized 关键字来实现互斥
 * 3 最常见的懒汉版本
 */
public final class SynchronizedSingleton {
    private static SynchronizedSingleton instance;

    private SynchronizedSingleton() {
        if (instance != null) {
            throw new IllegalStateException("单例实例已经初始化");
        }
    }

    public static synchronized SynchronizedSingleton getInstance() {
        if (instance == null) {
            instance = new SynchronizedSingleton();
        }
        return instance;
    }
}

同步版本的单例实现了懒加载,在多线程环境中也可以很好的工作,但是,效率很低,因为多数情况下都不需要同步。双重检查锁版本是对 Synchronized 版本的改进。

双重检查锁版(懒汉式)

/**
 * 双重检查锁单例
 * 1 懒加载;
 * 2 线程安全(通过锁机制来保证)
 * 相比 Synchronized 的写法会有性能上的提升,主要体现在不会每次获取实例都获取锁(只有在实例没有初始化的情况下,才会获取锁);
 */
public final class DoubleCheckLockingSingleton {

    private static volatile DoubleCheckLockingSingleton instance;

    /**
     * 私有构造方法
     */
    private DoubleCheckLockingSingleton(){
        // 判断单例是否已经初始化,来组织反射创建单例
        if (instance != null) {
            throw new IllegalStateException("单例对象已经初始化");
        }
    }

    /**
     * 单例方法
     *
     * @return
     */
    public static DoubleCheckLockingSingleton getInstance(){
        // 局部变量提升 25% 的性能?存疑 TODO 待考证
        DoubleCheckLockingSingleton result = instance;
        if (result == null) {
            synchronized (DoubleCheckLockingSingleton.class) {
                result = instance;
                if (result == null) {
                    instance = result = new DoubleCheckLockingSingleton();
                }
            }
        }
        return result;
    }

}

这种方式是懒加载常用的写法,在多线程环境中也可以保证单例的正确创建,getInstance 方法执行效率比 Synchronized 版本要高。

静态内部类版(懒汉式)

/**
 * 静态内部类延迟加载单例
 */
public final class OnDemandHolderSingleton {

    /**
     * 私有构造方法
     */
    private OnDemandHolderSingleton(){}

    /**
     * 获取单例实例
     *
     * @return
     */
    public static OnDemandHolderSingleton getInstance() {
        return SingletonHolder.instance;
    }

    /**
     * 静态内部类
     */
    private static class SingletonHolder {
        private static final OnDemandHolderSingleton instance = new OnDemandHolderSingleton();
    }
}

《Java并发编程实践》中推荐的单例写法,实现相比双重检查锁简单。适用于对静态域的延迟初始化;这种方式同样利用了 classLoader机制来保证 instance 初始化时的线程安全。内部类 SingletonHolder 只有在 OnDemandHolderSingleton.getInstance() 方法调用时,才会执行类加载,进而触发 instance 的创建。

枚举

/**
 * 枚举 : 枚举也是一种单例。
 */
public enum EnumSingleton {
    INSTANCE;
}

天生单例;不过这种写法很少见;

参考链接

  • 维基百科-单例模式
  • 菜鸟教程-单例模式
  • 推荐:Github-java-design-patterns

码云项目链接:https://gitee.com/maolv/java_code_example/tree/master/design_pattern/src/main/java/com/gitee/maolv/java/pattern/singleton

你可能感兴趣的:(设计模式之单例模式)