美文网首页
单例模式

单例模式

作者: printf200 | 来源:发表于2019-09-23 17:34 被阅读0次

    什么是单例

    确保任何情况下都绝对只有一个实例
    ServletContext、ServletConfig、ApplicationContext、DBpool

    任何情况下包括:多线程、并发、反射调用构造器

    官方:是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点

    隐藏其所有的构造方法
    属于创建型模式

    写法:
    懒汉式
    饿汉式
    注册式
    ThreadLocal单例

    枚举、double check、内部类

    目的:

    为什么要这么写?
    饿汉式:先吃饱再说
    优点:线程安全,逻辑简单、空间换时间
    缺点:如果这种方法对象被大量使用,不管用不用,都初始化,导致内存开销增加

    package cn.single.hungry;
    
    /**
     * 写法大量使用,类本身的实例不管你用不用,默认就赋值
     * 可能造成内存浪费
     */
    
    /*
    * 饿汉式
    * */
    public class HungrySingle {
    
        //不加final有可能被误操作覆盖掉
        private static final HungrySingle hungrySingleton;
    
        static {
            hungrySingleton = new HungrySingle();
        }
        private HungrySingle(){}
    
        public static HungrySingle getInstance(){
            return hungrySingleton;
        }
    }
    

    懒汉式:要调用的用的时候吃(赋值)
    简单写法:

    public class LazySingleton {
    
        private static LazySingleton instance = null;
    
        private LazySingleton(){
    
        }
    
        public static LazySingleton getInstance(){
            //第一次使用的时候,他会做判断
            if (null == instance){
                instance = new LazySingleton();
            }
            //第二次就直接返回
            return instance;
        }
    
    
    }
    
    

    优点:资源占用少,逻辑简单
    缺点:线程不安全(随机产生)

    不安全原因
    两个线程打印相同结果:
    1.线程按顺序执行(正常)
    2.线程同时进入(后者覆盖前者)
    两个线程打印不同结果
    线程同时进行(先后执行之后的逻辑)
    解决方式
    加锁

    public class LazySingleton {
    
        private static LazySingleton instance = null;
    
        private LazySingleton(){
    
        }
    
        public static   synchronized    LazySingleton getInstance(){
            //第一次使用的时候,他会做判断
            if (null == instance){
                instance = new LazySingleton();
            }
            //第二次就直接返回
            return instance;
        }
    
    
    }
    

    简单(synchronize)
    缺点:效率低利用率低

    doublecheck写法

        private volatile static LazyDoubleChecksSingleton instance = null;
    
        private LazyDoubleChecksSingleton(){
    
        }
    
        public static LazyDoubleChecksSingleton getInstance(){
            if (instance ==null){
            synchronized (LazyDoubleChecksSingleton.class){
                //第一次使用的时候,他会做判断
                if (null == instance){
                    instance = new LazyDoubleChecksSingleton();
                    //指令重排序的问题
                }
              }
            }
            //第二次就直接返回
            return instance;
        }
    

    缺点:可读性下降,不够优雅

    LazyinnerClassSingleton写法
    优点:优雅,利用天生的语法优势

    public class LazyinnerClassSingleton {
    
        private LazyinnerClassSingleton(){
    
        }
        public static final LazyinnerClassSingleton getInstance(){
            return LazyHolder.INSTANCE;
        }
        private static class  LazyHolder{
            private static final  LazyinnerClassSingleton INSTANCE = new LazyinnerClassSingleton();
        }
    }
    

    以上5种都有共同的缺点:
    private Singleton(){}
    它是骗人的,只按套路出牌
    破解方法(反射)

    public class RelectTest {
        public static void main(String[] args) {
            try {
                Constructor c= LazyinnerClassSingleton.class.getDeclaredConstructor();
                c.setAccessible(true);
                Object instance = c.newInstance();
                System.out.println(instance);
                Object instance1 = c.newInstance();
                System.out.println(instance1);
    //返回结果 
    //cn.single.lazy.LazyinnerClassSingleton@4554617c
    //cn.single.lazy.LazyinnerClassSingleton@74a14482
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    }
    

    注册式单例

    枚举式
    官方:有jdk为枚举保驾护航

    /**
     * 绝对信任线程安全
     * 高性能写法
     */
    
    //常量中去使用,常量不就是用来大家都能够共用吗?
    //通常在通用API中使用
    public enum  EnumSingleton {
        INSTANCE;
        private Object data;
        public Object getData(){
            return data;
        }
        public void setData(Object data){
            this.data = data;
        }
        public static EnumSingleton getInstance(){
            return INSTANCE;
        }
    
    }
    
    

    Spring IOC容器就是单例实现
    容器式单例
    默认值,类名首字母小写
    比较实用的

    总结:
    7种写法
    饿汉式
    static块、非static块
    懒汉式
    simple在方法上加synchronized关键字
    doublecheck写法
    innerclass

    注册式
    enum(Effctive Java推荐,最优雅的写法)
    Container容器,Spring IOC

    单例并不是最优雅就最好
    考虑实用性
    两个(饿汉式)
    最底层的代码对性能要求很高(enum,innderclass)
    单例对象非常多,使用根据实际情况,(spring ioc)

    相关文章

      网友评论

          本文标题:单例模式

          本文链接:https://www.haomeiwen.com/subject/iyabyctx.html