• 单例模式的不同实现方式效果差异


    单例模式

    单例模式是设计模式中最简单的一种。单例模式属于创建型模式,该类负责创建自己的对象,同时确保只有单个对象被创建。

    使用场景
    当需要保证一个类只有一个实例,并且全局获取到的实例相同。
    优点:

    • 避免对象频繁的创建与销毁,节省系统资源
    • 调用方便

    实现方法

    • 提供一个静态方法获取静态实例
    • 将构造函数私有化,禁止外部创建实例。
    public static class Singleton {
    
        private static final Singleton instance = new Singleton();
    
        private Singleton() {
        }
    
        public static Singleton getInstance() {
            return instance;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    调用方法

    Signalton signalton = Signalton.getInstance();
    
    • 1

    Java单例模式的几种实现方法

    1 静态常量

    静态常量方法通过声明时直接创建单例对象。
    线程安全
    优点:不需要加锁就可以达到线程安全
    缺点:如果不需要调用getInstance方法会无意义占用内存

    public static class HungrySingleton1 {
    
        private static final HungrySingleton1 instance = new HungrySingleton1();
    
        private HungrySingleton1() {
        }
    
        public static HungrySingleton1 getInstance() {
            return instance;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2 静态块初始化

    静态块初始化方法和静态常量方法一样,在调用getInstance前就完成创建单例对象。
    线程安全
    优点:不需要加锁就可以达到线程安全
    缺点:如果不需要调用getInstance方法会无意义占用内存

    public static class HungrySingleton2 {
        private static final HungrySingleton2 instance;
    
        static {
            instance = new HungrySingleton2();
        }
    
        private HungrySingleton2() {
        }
    
        public static HungrySingleton2 getInstance() {
            return instance;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    3 同步锁

    同步锁方法在调用getInstance方法时才创建对象,使用synchronized关键字来达到线程安全。
    线程安全
    优点:不会浪费内存
    缺点:效率低

    public static class LazySingleton2 {
        private static LazySingleton2 instance;
    
        private LazySingleton2() {
        }
    
        public static synchronized LazySingleton2 getInstance() {
            if (instance == null) {
                instance = new LazySingleton2();
            }
            return instance;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    4 双重检查

    双重检查方法是同步锁方法的改进,利用volatile关键字和使用synchronized关键字,做两次判断来达到效率和线程安全的兼顾。
    线程安全
    优点:不会浪费内存

    public static class LazySingleton4 {
        //利用volatile关键字
        private static volatile LazySingleton4 instance;
    
        private LazySingleton4() {
        }
    
        public static LazySingleton4 getInstance() {
            if (instance == null) {
                synchronized (LazySingleton4.class) {
                    if (instance == null) {
                        instance = new LazySingleton4();
                    }
                }
            }
            return instance;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    5 静态内部类

    静态内部类方法通过利用类加载机制来达到单例效果。
    线程安全
    优点:不会浪费内存

    public static class LazySingleton5 {
    
        private LazySingleton5() {
        }
    
        private static class SingleTon {
            private static final LazySingleton5 INSTANCE = new LazySingleton5();
        }
    
        public static LazySingleton5 getInstance() {
            return LazySingleton5.SingleTon.INSTANCE;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    6 枚举

    jdk1.5后支持的方法。
    线程安全
    优点:能防止反射操作
    缺点:不能继承类,但是可以实现其它接口

    enum LazySignalTon6 {
        INSTANCE;
    
        public void init() {
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    效率对比

    写了一段测试代码,在构造函数中sleep 200ms,然后分别统计第一次和执行一百万次getInstance的耗时。

    序号方案第一次耗时(ms)一百万次调用耗时(ms)一百亿次(ms)
    1静态常量2428.21344
    2静态初始化块20521238
    3同步锁20127.131178
    4双重检查2175.71347
    5静态内部类2165.31190
    6枚举10097.11231

    从结果上看,第一次初始化最快的是静态初始化块方案,最慢的是枚举方案。
    一百万次调用上,最慢的是同步锁方案,最快的是静态初始化块方案。除了同步锁,其它方案在调用效率上其实差不多。

  • 相关阅读:
    APP软件外包开发流程
    数字化会员留存怎么做?会员积分体系如何设计?
    混淆矩阵详解:评估深度神经网络性能的关键工具
    android_使用adb安装app包的详细步骤和可能遇到的问题(apk文件)
    七夕 跟mysql 数据库一起度过
    Docker常用命令
    esp32-C3 CAN接口使用
    【AntDesign】封装全局异常处理-全局拦截器
    Redis高可用三主三从集群部署
    IPv6协议基本概念
  • 原文地址:https://blog.csdn.net/Jun_P/article/details/126692837