zoukankan      html  css  js  c++  java
  • Java单例模式的几种实现

    转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/10845530.html

    一:静态内部类实现单例模式

      原理:通过一个静态内部类定义一个静态变量来持有当前类实例,在类加载时就创建好,在使用时获取。

      缺点:无法做到延迟创建对象,在类加载时进行创建会导致初始化时间变长。

    public class SingletonInner {
        private static class Holder {
            private static SingletonInner singleton = new SingletonInner();
        }
     
        private SingletonInner(){}
     
        public static SingletonInner getSingleton(){
            return Holder.singleton;
        }

    二:饿汉模式

      原理:创建好一个静态变量,每次要用时直接返回。

      缺点:无法做到延迟创建对象,在类加载时进行创建会导致初始化时间变长。

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

    三:懒汉模式

      原理:延迟创建,在第一次用时才创建,之后每次用到就返回创建好的。

      缺点:由于synchronized的存在,多线程时效率很低。

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

    四:双重校验锁懒汉模式

      原理:在getSingleton()方法中,进行两次null检查。这样可以极大提升并发度,进而提升性能。

    public class Singleton {
        private static volatile Singleton singleton = null;//使用valatile,使该变量能被所有线程可见
     
        private Singleton(){}
     
        public static Singleton getSingleton(){
            if(singleton == null){
                synchronized (Singleton.class){//初始化时,加锁创建
                    if(singleton == null){//为避免在加锁过程中被其他线程创建了,再作一次非空校验
                        singleton = new Singleton();
                    }
                }
            }
            return singleton;
        }    

    五:上述4种方式实现单例模式的缺点

      1、反序列化对象时会破环单例

      反序列化对象时不会调用getXX()方法,于是绕过了确保单例的逻辑,直接生成了一个新的对象,破环了单例。

      解决办法是:重写类的反序列化方法,在反序列化方法中返回单例而不是创建一个新的对象。

    public class Singleton implements java.io.Serializable {     
       public static Singleton INSTANCE = new Singleton();     
     
       protected Singleton() {     
       }  
     
       //反序列时直接返回当前INSTANCE
       private Object readResolve() {     
                return INSTANCE;     
          }    
    }

      2、在代码中通过反射机制,直接调用类的私有构造函数创建新的对象,会破环单例

      解决办法是:维护一个volatile的标志变量在第一次创建实例时置为false;重写构造函数,根据标志变量决定是否允许创建。

    private static volatile  boolean  flag = true;
    private Singleton(){
        if(flag){
        flag = false;   //第一次创建时,改变标志
        }else{
            throw new RuntimeException("The instance  already exists !");
        }


    六:枚举模式实现单例——将单例维护在枚举类中作为唯一实例

      原理:定义枚举类型,里面只维护一个实例,以此保证单例。每次取用时都从枚举中取,而不会取到其他实例。

    public enum  SingletonEnum {
        INSTANCE;
        private String name;
        public String getName(){
            return name;
        }
        public void setName(String name){
            this.name = name;
        }

      优点:

      1)使用SingletonEnum.INSTANCE进行访问,无需再定义getInstance方法和调用该方法。

      2)JVM对枚举实例的唯一性,避免了上面提到的反序列化和反射机制破环单例的情况出现:每一个枚举类型和定义的枚举变量在JVM中都是唯一的。 

        原因:枚举类型在序列化时仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过java.lang.Enum的valueOf方法来根据名字查找枚举对象

          同时,编译器禁止重写枚举类型的writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法。

      

      

  • 相关阅读:
    无线鼠标换电池了
    Jython Interactive Servlet Console YOU WILL NEVER KNOW IT EXECLLENT!!! GOOD
    Accessing Jython from Java Without Using jythonc
    jython podcast cool isnt't it?
    Python里pycurl使用记录
    Creating an Interactive JRuby Console for the Eclipse Environment
    微软为AJAX和jQuery类库提供CDN服务
    Download A File Using Cygwin and cURL
    What is JMRI?这个是做什么用的,我真没看懂但看着又很强大
    用curl 发送指定的大cookie的http/https request
  • 原文地址:https://www.cnblogs.com/ygj0930/p/10845530.html
Copyright © 2011-2022 走看看