单例模式的7种创建方式

匿名 (未验证) 提交于 2019-12-02 23:05:13

public final class SingletonObject1 {     private static final SingletonObject1 instance = new SingletonObject1();      private SingletonObject1() {     }       public static SingletonObject1 getInstance() {         return instance;     } } 

  饿汉式的创建方法关键在于 instance作为类变量直接得到了初始化,这种方法的优点在于多线程环境下能够百分百地保证同步,在多线程环境下不可能被实例化两次,但是instance若是被加载后很长一段时间后才使用,就意味着instance实例开辟的堆内存会驻留更长的时间,所以更优的创建方式应是伴随着懒加载的。

2.懒汉式

public final class SingletonObject2 {          private static  SingletonObject2 instance == null;      private SingletonObject2() {     }      public static SingletonObject2 getInstance() {         if (null == instance)             instance = new SingletonObject2();         return instance;     } } 

  所谓懒汉式就是在使用的时候才去创建,这样就可以避免类在初始化时提前创建,但是这种方式有一个很大的缺点,在多线程的环境下,若一开始因为线程上下文切换的原因,两个线程都通过了null==instance的if循环,这样就是new出两个实例,无法保证单例的唯一性,所以有下面第三种方法。

3.懒汉式+同步方法

public final class SingletonObject3 {         private static SingletonObject3 instance ;      private SingletonObject3() {     }      public synchronized static SingletonObject3 getInstance() {         if (null == instance)             instance = new SingletonObject3();         return instance;     } } 

  这种方法通过添加同步控制既满足了懒加载,又满足了instance实例的唯一性,但是,添加了同步控制后getInstance方法的调用是串行化的,效率较低,因此引出第四种创建方式---Double Check方式

4.Double-Check

public final class SingletonObject4 {      Socket socket; //模仿一些资源的实例化        private static SingletonObject4 instance ;    private SingletonObject4() {        this.socket = new Socket();    }    public static SingletonObject4 getInstance() {        if (null == instance) {            synchronized (SingletonObject4.class) {                if (null == instance)                    instance = new SingletonObject4();            }        }        return SingletonObject4.instance;    }}

  若有两个线程通过了第一个Check循环,进入第二个Check循环是串行化的,只能有一个线程进入,这样当这个线程创建完成后,另外的线程就无法通过第二个循环了,保证了实例的唯一性,随后的线程也不会通过第一个Check循环,也就不会有同步控制的环节了。但是,这种方法也伴随着一个缺点,它可能会引起空指针的异常。

  假设这个单例创建有一些其他的资源,例如Socket、Connection,这些资源在构造函数中也会被实例化,那样在创建单例的时候,就是要实例化自身还有Socket这些资源,那根据JVM的重排序和Happens-before原则,有可能会出现先实例化自身,再去实例化Socket这些资源,若在此时只实例化了自己的情况下,别的线程调用了这个单例中Socket这些资源的方法,而此时它们可能还没有被实例化,这样就会抛出空指针的异常,在此引出第五种创建方法----Volatile+Double-Check

5.Volatile+Double-Check

public final class SingletonObject5 {        private volatile static SingletonObject5 instance ;      private SingletonObject5() {     }       public static SingletonObject5 getInstance() {         if (null == instance) {             synchronized (SingletonObject5.class) {                 if (null == instance)                     instance = new SingletonObject5();             }         }         return SingletonObject5.instance;     } } 

  volatile关键字可以防止重排序的发生,在此不对volatile作详细介绍,通过volatile关键字,这种模式可以说是满足懒加载、多线程下单例的唯一性、安全性的。

6.Holder方式

public final class SingletonObject6{      private SingletonObject6() {     }      private static class InstanceHolder {         private static  SingletonObject6 instance = new SingletonObject6();     }      public static SingletonObject6 getInstance() {         return InstanceHolder.instance;     }     } 

  Holder这种方式是本人最喜欢的一种创建方式,它借助了类加载的特点,在SingletonObject6中并没有instance的静态成员,而是放置了静态内部类InstanceHolder之中,因此SingletonObject6的初始化过程中并不会实例化instance,当Holder被主动引用的时候才会进行实例化,而在instance被实例化时是会在Java程序编译器中收集至<cliinit>()方法中的,该方法是同步方法,且保证内存的可见性、原子性和顺序性,可以说是饿汉方式的优化版,这种创建方式是最为广泛的方式之一。

7.枚举法

  

public enum  EnumSingleton {     Instance;     public void method(){              } }

  枚举法是《Effective Java》中作者推荐的方式 ,这种方法极为简单,因为枚举类型本身是final的,不允许被继承,且同样是线程安全的,且只能被实例化一次和不用考虑序列化之类的问题,使用的时候可以直接EnumSingleton.Instance.method()就可以使用了,但是它不能实现懒加载,比如调用其中的静态方法也是会实例化Instance的,读者可以自行测试,但是也可以进行改造,让枚举充当Holder的角色增加懒加载的特性,代码如下

public final class SingletonObject7 {          private SingletonObject7() {}      private enum Singleton {         INSTANCE;          private final SingletonObject7 instance;          Singleton(){             instance = new SingletonObject7();         }          public SingletonObject7 getInstance() {             return instance;         }     }      public static SingletonObject7 getInstance() {         return Singleton.INSTANCE.getInstance();     } }

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!