• 设计模式:单例的五种实现及分析


        单例模式是设计模式中最简单的一种创建型模式,使用场景一般有:工具类对象、系统中只能存在一个实例对象的类、创建频繁或又耗时耗资源且又经常用到的对象等。如:JDK的Runtime类就是饥饿的单例模式,以及Spring容器管理的实例Bean默认也是饥饿单例,在容器启动时初始化,当然也可以设置为懒汉式(default-lazy-init="true")。再如程序中引入公共线程池,为防止多次创建线程池浪费资源,公共线程池也可以采用单例模式实现的。

    1、饥饿模式

        类加载时默认初始化,实现最简单但也有一定的局限性,1、可能类加载时某些资源还未准备好,2、多个不同的自定义类加载器同时加载时可能导致重复初始化

    public class HungrySingleton {
    
        private static final HungrySingleton instance = new HungrySingleton ();
    
        private HungrySingleton () {
            // 一定要记得私有化构造器
        }
    
        public static HungrySingleton getInstance() {
            return instance;
        }
    }
    

    2、懒汉模式(要注意线程安全问题)

    public class LazySingleton {
    
        private static LazySingleton instance = null;
    
        private LazySingleton () { }
    
        /**
         * 一定要记得加synchronized
         */
        public static synchronized LazySingleton getInstance() {
            if(null == instance) {
                instance = new LazySingleton ();
            }
            return instance;
        }
    }
    

    3、双重检测锁模式

    要注意线程安全隐患:多线程环境下获取到未初始化完全的实例对象,用volatile修饰对象可以防止该问题,详尽分析见下面注释

    public class DoubleCheckSingleton {
    
        /**
         * TODO 这里主要利用了volatile的“顺序性”,保证对instance的写不会发生指令重排而引发其他线程获取到未初始化完全的对象
         * TODO 就算没有volatile,“可见性”也可由synchronized保证
         */
        private static volatile DoubleCheckSingleton instance = null;
    
        private DoubleCheckSingleton() { }
    
        public static DoubleCheckSingleton getInstance() {
            if (null == instance) {
                synchronized (DoubleCheckSingleton.class) {
                    if (null == instance) {
                        // new DoubleCheckSingleton() 不是原子操作,大体步骤如下
                        // memory = allocate();    // 1:分配对象的内存空间
                        // ctorInstance(memory);   // 2:初始化对象
                        // instance = memory;      // 3:设置instance指向刚才分配的内存地址
                        // 伪代码中的2和3之间,可能会被重排序,所以如果执行顺序按1,3,2的话,线程在执行完3后时间片用完切换线程则可能出现未初始化的对象
                        // 如果用volatile来修饰则会产生写读屏障storeload,禁止2,3的指令重拍,从而防止出现未初始化完全的问题
                        // TODO 就算时间片用完切换其他线程也进入不了synchronized代码块,因为原来代码块还没执行完成,所以不会释放锁,也不会出现多次实例化
                        instance = new DoubleCheckSingleton();
                    }
                }
            }
            return instance;
        }
    

    4、静态内部类模式(推荐使用:由JVM保证线程安全且代码简单不容易出错)

    public class InnerClassSingleton {
    
        private InnerClassSingleton() { }
    
        private static class NestClass {
            private static final InnerClassSingleton instance = new InnerClassSingleton();
        }
    
        public static InnerClassSingleton getInstance() {
            // javac编译后,NestClass是一个单独的.class文件,
            // 加载InnerClassSingleton.class的时候不会自动加载NestClass.class文件
            // 当调用NestClass.instance的时候才会触发JVM加载NestClass类
            return NestClass.instance;
        }
    }
    

    5、枚举模式

    跟静态内部类的原理是一样的,JVM会保证enum不能被反射并且构造器方法只执行一次,因此该单例是线程安全的

    public class EnumSingleton {
    
        private EnumSingleton() { }
    
        private enum InnerEnum {
            /**
             * 占位枚举值
             */
            enumFactory;
            private EnumSingleton instance;
    
            /**
             * TODO 注意这里是枚举类的构造器
             */
            private InnerEnum() {
                instance = new EnumSingleton();
            }
            public EnumSingleton getInstance() {
                return instance;
            }
        }
    
        public static EnumSingleton getInstance() {
            return InnerEnum.enumFactory.getInstance();
        }
    }
    
  • 相关阅读:
    C# 模式&模式匹配
    CentOS7下安装 dotnet sdk
    PLSQL 设置
    Visual Studio Code 开发 .NetCore 插件列表
    .net core 服务生命周期
    从 Asp.Net Core 2.2 迁移到 3.0 的方案
    CSRedisCore 介绍
    Entity Framework 命令
    DataAnnotations 模型配置
    依赖注入学习(理论)
  • 原文地址:https://www.cnblogs.com/ocean234/p/11176954.html
Copyright © 2020-2023  润新知