设计模式之《单例模式》的多种实现--Java

    技术2022-07-10  102

    Java单例模式如何实现

     

    单例模式属于创建型设计模式,该类设计模式抽象了对象创建、组织的过程,将目标类的信息进行封装,仅向外部提供获取对象的接口。

    单例模式确保系统中只包含一个目标类的实例,负责完成其实例化并向外部提供该实例。一般可用于文件创建、数据库连接等辅助类,实现对系统资源的控制。

    一般来说,单例模式在实现上具有私有构造方法、私有静态变量、公有返回接口等特点。以下为几种常见的单例模式实现方法。

    1.饿汉模式

    饿汉模式在类加载时完成实例的创建,不存在线程安全的问题,缺点在于即使未被引用也始终会存在该类的实例。

    class Singleton{    private static final Singleton singleton = new Singleton(); ​    private Singleton(){} ​    public static Singleton getInstance(){        return singleton;   } }

    注意final,保证引用的实例不变。

    2.懒汉模式

    懒汉模式在初次获取实例时完成创建并返回,可以实现懒加载,但其缺点在于初次获取时可能需要更长的时间;适用于创建消耗大、实例使用少的情况,存在线程安全问题。

    class Singleton{    private static Singleton1 instance = null; ​    private Singleton() { } ​    public static Singleton getInstance() {        if (null == instance) {            instance = new Singleton();       }        return instance;   } }

    3.双重检验锁

    双重检验锁是适用于多线程模式下的懒汉模式,相比于直接对整个getInstance()方法加锁,可以减少大量进入临界区的性能消耗。

    class Singleton{    private static volatile Singleton singleton; ​    private Singleton(){} ​    public static Singleton getInstance(){        if (singleton == null){            synchronized (Singleton.class){                if (singleton == null){                    singleton = new Singleton();               }           }       }        return singleton;   } }

    第一层判断是为了在实例已经初始化时直接返回,第二层锁可以防止同时进入临界区的不同线程多次实例化对象。

    注意volatile:

    由于singleton = new Singleton();并非原子操作,而是由分配内存空间、初始化对象以及将引用指向堆对象三个步骤完成,而在JVM执行时可能产生重排序,使得引用变量指向了未初始化的堆对象,导致另一个线程判断失败(第一层)获取到该未初始化的实例;如下图所示:

    volatile关键字可以保证线程之间的可见性及有序性,使所有写操作先于读操作执行,避免上述问题。

    4.利用静态内部类

    线程安全:与懒汉模式相同,利用类的加载保证单例对象只被实例化一次;

    懒加载:由于一个类只有在其静态成员初次被引用时才会被加载,即调用Singleton.getInstance()时内部类SingletonHolder才会被加载,同时初始化instance。

    反射安全:通过反射不能从外部类中获取内部类的属性,即不可对instance进行修改。

    public class Singleton{    private static class SingletonHolder{        public static Singleton instance = new Singleton();   }    private Singleton(){}    public static Singleton getInstance(){        return SingletonHolder.instance;   } }

     

    5.利用枚举Enum

    在Java中,枚举可以看作一个类,其中也可以定义方法;而枚举中的变量可以看作该类的一个实例。枚举的构造方法限制为私有,在初次访问枚举实例时执行;而枚举中的实例定义为static final,从而只可实例化一次。

    class SingleTon{} ​ enum SingleEnum{    myEnum;          // 枚举实例    private SingleTon singleTon;    SingleEnum() {       // 构造方法,默认为private        singleTon = new SingleTon();   }    public SingleTon getSingle(){        return singleTon;   } }

    实例的获取:

    SingleTon s=SingleEnum.myEnum.getSingle();

     

     

    Processed: 0.009, SQL: 12