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等方法。

      

      

  • 相关阅读:
    logstash 配置 logstash-forwarder (前名称:lumberjack)
    你不知道的if,else
    css样式
    表格 表单
    学习第一天练习
    唯有作茧自缚,方可破茧成蝶
    第一周复习二 (CSS样式表及其属性)
    第一周复习一 ( HTML表单form)
    汉企第一天小记
    C语言 -- register关键字
  • 原文地址:https://www.cnblogs.com/ygj0930/p/10845530.html
Copyright © 2011-2022 走看看