• 详解设计模式:单例模式


    单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

    在单例模式中,内存中只会创建且仅创建一次对象在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象。

    本篇内容包括:关于单例模式、单例模式-饿汉式实现、单例模式-懒汉式实现



    一、关于单例模式

    1、关于单例模式

    单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

    在单例模式中,内存中只会创建且仅创建一次对象在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象。

    Ps:在 Spring 中,每个 Bean 默认就是单例的,这样的优点是 Spring 容器可以管理这些 Bean 的生命周期,决定什么时候创建出来,什么时候销毁,销毁的时候如何处理等等。

    2、关于单例模式的类型

    单例模式有两种类型:

    • 懒汉式:在真正需要使用对象时才去创建该单例类对象。在开发中如果对内存要求非常高,那么使用懒汉式写法,可以在特定时候才创建该对象
    • 饿汉式:在类加载时已经创建好该单例对象,等待被程序使用。如果对内存要求不高使用饿汉式写法,因为简单不易出错,且没有任何并发安全和性能问题
    3、关于单例模式的优缺点

    # 单例模式的优点

    • 减少内存开支:由于单例模式在内存中只有一个实例,特别是在对象需要频繁地创建销毁的场景下,单例模式就非常明显;
    • 减少系统的性能开销:当一个对象产生需要比较多的资源时,如读取配置,产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决;
    • 避免对资源的多重占用:例如一个写文件操作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作;
    • 优化和共享资源访问:单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如,可以设计一个单例类,负责所有数据表的映射处理。

    # 单例模式的缺点

    • 单例模式没有抽象层,扩展很困难,若要扩展,除了修改代码基本上没有第二种途径可以实现。
    • 单例类的职责过重,在一定程度上违背了“单一职责原则”。
    • 滥用单例将带来一些负面问题
      • 如:为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;
      • 又比如:在多个线程中操作单例类的成员时,但单例中并没有对该成员进行线程互斥处理。

    二、单例模式-饿汉式实现

    所谓饿汉式也就是在类加载的时候直接 new 出一个对象来,不管以后用不用得到,是一种以空间换取时间的策略。

    1、饿汉式(基于静态变量)实现
    public class Singleton {
    
        /**
         * 定义一个变量来存储创建好的实例,直接在这里创建实例,只能创建一次,
         * static 变量在类加载时进行初始化,并且只被初始化一次。
         */
        private static final Singleton INSTANCE = new Singleton();
    
        /**
         * 私有化构造方法,可以在内部控制创建实例的数目,防止在外部创建
         */
        private Singleton() {
        }
    
        /**
         * 定义一个方法为客户端提供类实例,方法上加static将该方法变为静态,目的是不需要对象实例就可以在外部直接通过类来调用
         */
        public static Singleton getInstance() {
            // 直接使用已经创建好的实例
            return INSTANCE;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    2、饿汉式(基于静态代码块)实现
    public class Singleton {
    
        /**
         * 在成员位置创建该类的对象
         */
        private static final Singleton INSTANCE;
    
        /**
         * 私有构造方法
         */
        private Singleton() {
        }
        
        static {
            //  静态代码块随着类的加载而加载,并且只加载一次,它的优先级与静态成员变量一致,谁先声明谁就先执行
            INSTANCE = new Singleton();
        }
    
        /**
         * 对外提供静态方法获取该对象
         */
        public static Singleton getInstance() {
            return INSTANCE;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    3、饿汉式(基于枚举代码块)实现
    public enum Singleton {
    
        /**
         * Java 的枚举类型实质上时功能齐全的类,因此可以有自己的属性和方法
         * 定义一个枚举的元素,它就代表了 Singleton 的一个实例
         */
        INSTANCE;
    
        /**
         * 单例自己的操作函数
         */
        public void singletonOperation() {
            // 功能处理
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    三、单例模式-懒汉式实现

    顾名思义,懒汉式指只有当该实例被使用到的时候才会创建。

    1、懒汉式(线程不安全版本)实现
    public class Singleton {
    
        /**
         * 私有构造方法
         */
        private Singleton() {
        }
    
        /**
         * 在成员位置声明Singleton类型的静态变量,并没有进行对象的赋值操作
         */
        private static Singleton instance;
    
        /**
         * 当调用 getInstance() 方法获取 Singleton 类的对象的时候才创建 Singleton 类的对象,这样就实现了懒加载的效果。
         *
         * @return Singleton
         */
        public static Singleton getInstance() {
    
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    2、懒汉式(同步方法版本)实现
    public class Singleton {
    
        /**
         * 私有构造方法
         */
        private Singleton() {
        }
    
        /**
         * 在成员位置创建该类的对象
         */
        private static Singleton instance;
    
        /**
         * 对外提供静态方法获取该对象,加了synchronized关键字,线程安全(但是导致该方法的执行效果特别低)
         *
         * @return Singleton
         */
        public static synchronized Singleton getInstance() {
    
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    3、懒汉式(双重检验锁版本)实现
    public class Singleton {
    
        /**
         * 私有构造方法
         */
        private Singleton() {
        }
    
        /**
         * 在成员位置创建该类的对象
         * 使用 volatile :避免 JVM 在实例化对象的时候会进行优化和指令重排序操作而导致空指针问题
         */
        private static volatile Singleton instance;
    
        /**
         * 对外提供静态方法获取该对象
         *
         * @return Singleton
         */
        public static Singleton getInstance() {
            // 第一次判断,如果instance不为null,不进入抢锁阶段,直接返回实例
            if (instance == null) {
                synchronized (Singleton.class) {
                    // 抢到锁之后再次判断是否为null
                    if (instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    4、懒汉式(静态内部类版本)实现
    public class Singleton {
    
        /**
         * 私有构造方法
         */
        private Singleton() {
        }
    
        private static class SingletonHolder {
            private static final Singleton INSTANCE = new Singleton();
        }
    
        /**
         * 对外提供静态方法获取该对象
         * 第一次加载 Singleton 类时不会去初始化 INSTANCE,只有第一次调用 getInstance,虚拟机加载 SingletonHolder,初始化 INSTANCE
         * 这样不仅能确保线程安全,也能保证 Singleton 类的唯一性
         *
         * @return Singleton
         */
        public static Singleton getInstance() {
            return SingletonHolder.INSTANCE;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    静态内部类单例模式是一种优秀的单例模式,是开源项目中比较常用的一种单例模式。在没有加任何锁的情况下,保证了多线程下的安全,并且没有任何性能影响和空间的浪费。

  • 相关阅读:
    超前预告 | 云原生?大模型?这届乌镇双态IT大会亮点有点多
    聊聊 mysql 事务?(三:从redo log恢复数据)
    freertos动态和静态任务创建的区别
    Redis简单介绍以及在Springboot中使用redis
    PHP将PDF转成多个PNG文件
    深入理解 Django 信号机制
    1363. 形成三的最大倍数 贪心
    108 使用Ajax请求获取PHP自制接口
    Mybatis-Plus前后端分离多表联查模糊查询分页
    一文带你读懂:TCP连接的三次握手和四次挥手(下篇)
  • 原文地址:https://blog.csdn.net/weixin_45187434/article/details/128061347