单例模式的关键点
构造方法不对外开放,为private
确保单例类只有一个对象,尤其是多线程模式下
通过静态方法或枚举返回单例对象
确保单例类在反序列化是不会重新创建新的对象
单例模式的五种实现方式
1、饿汉式 (线程安全,调用效率高,但是不能延时加载)
public class Singleton1 { /* * 饿汉式是在声明的时候就已经初始化Singleton1,确保了对象的唯一性 * * 声明的时候就初始化对象会浪费不必要的资源 **/ private static Singleton1 instance = new Singleton1(); private Singleton1() { } // 通过静态方法或枚举返回单例对象 public Singleton1 getInstance() { return instance; } }
一上来就把单例对象创建出来了,要用的时候直接返回即可,这种可以说是单例模式中最简单的一种实现方式。但是问题也比较明显。单例在还没有使用到的时候,初始化就已经完成了。也就是说,如果程序从头到位都没用使用这个单例的话,单例的对象还是会创建。这就造成了不必要的资源浪费。所以不推荐这种实现方式。
2.懒汉式 (线程安全,调用效率不高,但是能延时加载)
public class SingletonDemo2 { //类初始化时,不初始化这个对象(延时加载,真正用的时候再创建) private static SingletonDemo2 instance; //构造器私有化 private SingletonDemo2(){} //方法同步,调用效率低 public static synchronized SingletonDemo2 getInstance(){ if(instance==null){ instance=new SingletonDemo2(); } return instance; } }
3、Double CheckLock 双重锁判断机制 DCL 也就是(由于JVM底层模型原因,偶尔会出问题,不建议使用)
public class Singleton3 { private static Singleton3 instance; private Singleton3() {} /** * 两次判空,第一次判空是为了不必要的同步,第二次判空为了在instance 为 null 的情况下创建实例 * 既保证了线程安全且单例对象初始化后调用getInstance又不会进行同步锁判断 * * 优点:资源利用率高,效率高 * 缺点:第一次加载稍慢,由于java处理器允许乱序执行,偶尔会失败 * * @return */ public static Singleton3 getInstance() { if (instance == null) { synchronized (Singleton3.class) { if (instance == null) { instance = new Singleton3(); } } } return instance; } }
4、静态内部类实现模式(线程安全,调用效率高,可以延时加载)
public class Singleton4 { /* * 当第一次加载Singleton类时并不会初始化SINGLRTON,只有第一次调用getInstance方法的时候才会初始化 SINGLETON * 第一次调用getInstance 方法的时候虚拟机才会加载SingletonHoder类,这种方式不仅能够保证线程安全,也能够保证对象的唯一 * 还延迟了单例的实例化,所有推荐使用这种方式 **/ private Singleton4() {} public Singleton4 getInstance() { return SingletonHolder.SINGLETON; } private static class SingletonHolder { private static final Singleton4 SINGLETON = new Singleton4(); } }
5、枚举类(线程安全,调用效率高,不能延时加载,可以天然的防止反射和反序列化调用)
public enum SingletonEnum { /** * 枚举元素本身就是单例 * 默认枚举实例的创建时线程安全的,且在任何一种情况下它都是单例,包括反序列化 * */ INSTANCE; }
如何选用
单例对象 占用资源少,不需要延时加载,枚举 好于 饿汉
单例对象 占用资源多,需要延时加载,静态内部类 好于 懒汉式
未经允许请勿转载:程序喵 » 常用的五种单例模式实现方式