美文网首页
单例模式 学习

单例模式 学习

作者: fdsun | 来源:发表于2021-01-20 12:27 被阅读0次

    单例模式

    单例模式(Singleton Pattern)是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。
    隐藏其所有的构造方法。
    属于创建型模式。

    适用场景:

    确保任何情况下都绝对只有一个实例。

    单例模式的优点:

    在内存中只有一个实例,减少了内存开销。
    可以避免对资源的多重占用。
    设置全局访问点,严格控制访问。

    单例模式的缺点:

    没有接口,扩展困难。
    如果要扩展单例对象,只有修改代码,没有其他途径。

    单例模式的常见写法:

    1、饿汉式单例
    2、懒汉式单例
    3、注册式单例
    4、ThreadLocal单例

    饿汉式单例:

    在单例类首次加载时就创建实例
    缺点:浪费内存空间

    饿汉式单例
    public class HungrySingleton {
        private static final HungrySingleton hungrySingleton = new HungrySingleton();
        private HungrySingleton() {
        }
        public static HungrySingleton getInstance() {
            return hungrySingleton;
        }
    }
    
    /**
     * 饿汉式静态块单例
     */
    public class HungryStaticSingleton {
        private static final HungryStaticSingleton hungrySingleton;
            static {
            hungrySingleton = new HungryStaticSingleton();
        }
        private HungryStaticSingleton() {
        }
        public static HungryStaticSingleton getInstance() {
            return hungrySingleton;
        }
    }
    

    懒汉式单例:

    被外部类调用时才创建实例

    懒汉式单例
    public class LazySimpleSingleton {
        private LazySimpleSingleton() {
        }
        private static LazySimpleSingleton lazy = null;
        // 存在性能问题  -> DoubleCheck
        public synchronized static LazySimpleSingleton getInstance() {
            if (lazy == null) {
                lazy = new LazySimpleSingleton();
            }
            return lazy;
        }
    }
    
    public class LazyDoubleCheckSingleton {
        private volatile static LazyDoubleCheckSingleton lazy = null;
    
        private LazyDoubleCheckSingleton() {
        }
        // 适中的方案
        // 双重检查锁
        public static LazyDoubleCheckSingleton getInstance() {
            if (lazy == null) {
                synchronized (LazyDoubleCheckSingleton.class) {
                    if (lazy == null) {
                        lazy = new LazyDoubleCheckSingleton();
                        //  cpu执行时候会转换成JVM指令执行
                        // 指令重排序的问题, volatile
                        //1.分配内存给这个对象
                        //2.初始化对象
                        //3.设置lazy指向刚分配的内存地址
                        //4.初次访问对象
                    }
                }
            }
            return lazy;
        }
    }
    
    /**
     * 懒汉式单例
     * 这种形式兼顾饿汉式的内存浪费,也兼顾synchronized性能问题
     * 完美地屏蔽了这两个缺点
     * <p>
     * 全程没有用到synchronized
     * 性能最优的一种写法
     */
    public class LazyInnerClassSingleton {
            private LazyInnerClassSingleton() {
            // 防止使用反射恶意调用,破坏单例
            if (LazyHolder.LAZY != null) {
                throw new RuntimeException("不允许创建多个实例");
            }
        }
    
        /**
         * 懒汉式单例
         * LazyHolder 里面的逻辑需要等到外部方法调用时才执行
         * 巧妙利用了内部类的特性
         * JVM底层独行逻辑,完美地避免了线程安全阿题
         */
        public static final LazyInnerClassSingleton getInstance() {
            //在返回结果以前,一定会先加载内部类
            return LazyHolder.LAZY;
        }
    
        // 默认不加载
        private static class LazyHolder {
            private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
        }
    }
    

    注册式单例:

    将每一个实例都缓存到统一的容器中,使用唯一标识获取实例

    注册式单例
    /**
     * 枚举单例
     */
    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;
        }
    }
    
    public class ContainerSingleton {
        private ContainerSingleton() {
        }
    
        private static Map<String, Object> ioc = new ConcurrentHashMap<String, Object>();
    
        public static Object getInstance(String className) {
            synchronized (ioc) {
                if (!ioc.containsKey(className)) {
                    Object obj = null;
                    try {
                        obj = Class.forName(className).newInstance();
                        ioc.put(className, obj);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    return obj;
                } else {
                    return ioc.get(className);
                }
            }
        }
    }
    

    ThreadLocal单例:

    保证线程内部的全局唯一,且天生线程安全

    ThreadLocal单例
    public class ThreadLocalSingleton {
        private static final ThreadLocal<ThreadLocalSingleton> THREAD_LOCAL_INSTANCE =
                ThreadLocal.withInitial(ThreadLocalSingleton::new);
    
        private ThreadLocalSingleton() {
        }
    
        public static ThreadLocalSingleton getInstance() {
            return THREAD_LOCAL_INSTANCE.get();
        }
    }
    

    学习单例模式的知识重点总结:

    1、私有化构造器
    2、保证线程安全
    3、延迟加载
    4、防止序列化和反序列化破坏单例
    5、防御反射攻击单例

    相关文章

      网友评论

          本文标题:单例模式 学习

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