• JAVA设计模式--创建型模式--单例模式


    1.单例设计模式介绍

    所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在-一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。比如Hibernate的SessionFactory,它充当数据存储源的代理,并负责创建Session对象。SessionFactory并不 是轻量级的,一般情况下,一个项目通常只需要一个SessionFactory就够,这是就会使用到单例模式

    1.1意图

    保证一个类仅有一个实例,并提供一个访问它的全局访问点。

    1.2主要解决

    一个全局使用的类频繁地创建与销毁。

    1.3何时使用

    当您想控制实例数目,节省系统资源的时候。

    1.4如何解决

    判断系统是否已经有这个单例,如果有则返回,如果没有则创建。

    1.4关键代码

    构造函数是私有的。

    1.5 优点和缺点

    优点:

    1. 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
    2. 避免对资源的多重占用(比如写文件操作)。
      缺点:
    3. 没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

    2.单例设计模式八种方式

    1. 饿汉式(静态常量)
    2. 饿汉式(静态代码块)
    3. 懒汉式(线程不安全)
    4. 懒汉式(线程安全,同步方法)
    5. 懒汉式(线程安全,同步代码块)(错误示例缺陷大)
    6. 双重检查(一般推荐)
    7. 静态内部类(非常推荐)
    8. 枚举(非常推荐)

    2.1 饿汉式(静态常量)

    2.1.1实现方式:

    package com.xql.designpattern.controller.singleton;
    
    /**
     * 饿汉式(静态常量)
     *
     * @author 
     * @date 2022/08/16 21:18
     **/
    public class Singleton01 {
    
    
        //1.构造器私有化,外部不能直接new获取
        private Singleton01() {
        }
    
        //2.本类内部创建对象实例
        private final static Singleton01 instance= new Singleton01();
    
        //3.提供公有的静态方法,返回实例对象
        public static Singleton01 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.1.2优缺点说明:

    在这里插入图片描述

    2.2饿汉式(静态代码块)

    2.2.1实现方式:

    package com.xql.designpattern.controller.singleton;
    
    /**
     * 饿汉式(静态代码块)
     *
     * @author 
     * @date 2022/08/16 21:27
     **/
    public class Singleton02 {
    
        //1.构造器私有化,外部不能直接new获取
        private Singleton02(){
    
        }
    
        //2.本类内部创建对象实例
        private  static Singleton02 instance;
    
    
        //3.在静态代码块中 创建单例对象
        //静态块用于初始化类,为类的属性初始化。每个静态代码块只会执行一次。
        // 静态代码块随着类加载而加载,有多个静态代码块的,按代码块前后顺序加载。
        // 由于JVM在加载类时会执行静态代码块,所以静态代码块先于主方法执行。
        static {
            instance = new Singleton02();
        }
    
        //4.提供公有的静态方法,返回实例对象
        public static Singleton02 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
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    2.2.2优缺点说明:

    在这里插入图片描述

    2.3懒汉式(线程不安全)

    2.3.1实现方式:

    package com.xql.designpattern.controller.singleton;
    
    /**
     * 懒汉式(线程不安全)
     *
     * @author 
     * @date 2022/08/17 08:23
     **/
    public class Singleton03 {
    
        //1.构造器私有化,外部不能直接new获取
        private Singleton03(){
    
        }
    
        //2.本类内部创建对象
        private static Singleton03 singleton03;
    
    
        //3.提供一个静态的公有方法 当第一次使用该方法去创建instance
        public static Singleton03 getInstance(){
            if (singleton03 == null){
                singleton03 = new Singleton03();
            }
            return singleton03;
        }
    
    
    }
    
    
    • 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

    2.3.2优缺点说明:

    在这里插入图片描述

    2.4懒汉式(线程安全,同步方法)

    2.4.1实现方式:

    package com.xql.designpattern.controller.singleton;
    
    /**
     * 懒汉式(线程安全 同步方法)
     *
     * @author 
     * @date 2022/08/17 08:32
     **/
    public class Singleton04 {
        //1.构造器私有化,外部不能直接new获取
        private Singleton04(){
    
        }
    
        //2.本类内部创建对象
        private static Singleton04 singleton04;
    
    
        //3.提供一个静态的公有方法 当第一次使用该方法去创建instance
        public static synchronized Singleton04 getInstance(){
            if (singleton04 == null){
                singleton04 = new Singleton04();
            }
            return singleton04;
        }
    }
    
    
    • 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

    2.4.2优缺点说明:

    在这里插入图片描述

    2.5懒汉式(线程安全,同步代码块 错误方式)

    2.5.1实现方式:

    package com.xql.designpattern.controller.singleton;
    
    /**
     * 懒汉式(线程安全 同步代码块 )
     *
     * @author 
     * @date 2022/08/17 09:09
     **/
    public class Singleton05 {
        //1.构造器私有化,外部不能直接new获取
        private Singleton05(){
    
        }
    
        //2.本类内部创建对象
        private static Singleton05 singleton05;
    
    
        //3.提供一个静态的公有方法 当第一次使用该方法去创建instance
        //同步代码块加锁
        public static  Singleton05 getInstance(){
            if (singleton05 == null){
                synchronized(Singleton05.class){
                    singleton05 = new Singleton05();
                }
            }
            return singleton05;
        }
    }
    
    
    • 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

    2.5.2优缺点说明:

    在这里插入图片描述

    2.6双重检查(一般推荐)

    2.6.1实现方式:

    package com.xql.designpattern.controller.singleton;
    
    /**
     * 双重检查
     *
     * @author 
     * @date 2022/08/17 09:45
     **/
    public class Singleton06 {
    
        //1.本类内部创建对象 +volatile
        //volatile的作用有三点。
        // 1.保证可见性(大概意思就是多线程下每个线程执行完成会去通知主线程)
        // 2.不能保证原子性所以加了synchronized。(大家可以去写多线程累加每次+1 发现只加volatile结果不对 不能保证原子性)
        // 3.禁止指令重排
        private static volatile Singleton06 singleton06;
    
        //1.构造器私有化,外部不能直接new获取
        private Singleton06(){
    
        }
        //3.提供一个静态的公有方法 当第一次使用该方法去创建instance
        //同步代码块加锁
        public static  Singleton06 getInstance(){
            if (singleton06 == null){
                synchronized(Singleton06.class){
                    singleton06 = new Singleton06();
                }
            }
            return singleton06;
        }
    }
    
    
    • 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
    • 33

    2.6.2优缺点说明:

    在这里插入图片描述

    2.6.3 volatile:

    补充:volatile是Java虚拟机提供的轻量级的同步机制,它有3个特性:

    1. 保证可见性
    2. 不保证原子性
    3. 禁止指令重排
      详细尽量写在下一篇博客专门说volatile

    2.7 静态内部类(非常推荐)

    2.7.1实现方式:

    package com.xql.designpattern.controller.singleton;
    
    /**
     * 静态内部类
     *
     * @author 
     * @date 2022/08/17 10:07
     **/
    public class Singleton07 {
    
        
        //1.构造器私有化,外部不能直接new获取
        private Singleton07(){
        }
    
        //静态内部类 该类有一个静态熟悉Singleton07  直在类加载时候执行一次  JVM保证线程安全
        private static class SingletonInstance{
            private static final Singleton07 INSTANCE = new Singleton07();
        }
    
        //3.提供一个静态的公有方法
        public static Singleton07 getInstance(){
            return SingletonInstance.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

    2.7.2优缺点说明:

    在这里插入图片描述

    2.7.3 静态内部类

    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述

    2.8 枚举(非常推荐)

    2.8.1实现方式:

    package com.xql.designpattern.controller.singleton;
    
    /**
     * 枚举
     *
     * @author 
     * @date 2022/08/17 10:19
     **/
    public enum Singleton08 {
    
        INSTANCE;
    
        // 使用SingleInstance.INSTANCE.fun1();
        public void fun1() {
            // do something }
        }
    }
    
    //测试调用
    class Test{
        public static void main(String[] args) {
            Singleton08 instance = Singleton08.INSTANCE;
            Singleton08.INSTANCE.fun1();
        }
    }
    
    
    • 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.8.2优缺点说明:

    在这里插入图片描述

    2.8.3枚举:

    在这里插入图片描述

    3.一些源码用到的单例

    3.1 JDK的Runtime(饿汉式 静态常量)

    在这里插入图片描述
    因为很多地方用到Runtime 所以可以在类转载时候去创建一次

    3.2 Spring的Bean默认是Singleton

    主要是两种配置方式
    在这里插入图片描述
    Spring是借助ConcurrentHashMap来实现单例注册表。Spring 实现单例的核心代码如下:
    在这里插入图片描述

    3.3java.util.logging的Logger

    在这里插入图片描述

     public Logger getParent() {
            // Note: this used to be synchronized on treeLock.  However, this only
            // provided memory semantics, as there was no guarantee that the caller
            // would synchronize on treeLock (in fact, there is no way for external
            // callers to so synchronize).  Therefore, we have made parent volatile
            // instead.
            return parent;
        }
    
        /**
         * Set the parent for this Logger.  This method is used by
         * the LogManager to update a Logger when the namespace changes.
         * <p>
         * It should not be called from application code.
         * <p>
         * @param  parent   the new parent logger
         * @throws  SecurityException  if a security manager exists and if
         *          the caller does not have LoggingPermission("control").
         */
        public void setParent(Logger parent) {
            if (parent == null) {
                throw new NullPointerException();
            }
    
            // check permission for all loggers, including anonymous loggers
            if (manager == null) {
                manager = LogManager.getLogManager();
            }
            manager.checkPermission();
    
            doSetParent(parent);
        }
    
        // Private method to do the work for parenting a child
        // Logger onto a parent logger.
        private void doSetParent(Logger newParent) {
    
            // System.err.println("doSetParent \"" + getName() + "\" \""
            //                              + newParent.getName() + "\"");
    
            synchronized (treeLock) {
    
                // Remove ourself from any previous parent.
                LogManager.LoggerWeakRef ref = null;
                if (parent != null) {
                    // assert parent.kids != null;
                    for (Iterator<LogManager.LoggerWeakRef> iter = parent.kids.iterator(); iter.hasNext(); ) {
                        ref = iter.next();
                        Logger kid =  ref.get();
                        if (kid == this) {
                            // ref is used down below to complete the reparenting
                            iter.remove();
                            break;
                        } else {
                            ref = null;
                        }
                    }
                    // We have now removed ourself from our parents' kids.
                }
    
                // Set our new parent.
                parent = newParent;
                if (parent.kids == null) {
                    parent.kids = new ArrayList<>(2);
                }
                if (ref == null) {
                    // we didn't have a previous parent
                    ref = manager.new LoggerWeakRef(this);
                }
                ref.setParentRef(new WeakReference<>(parent));
                parent.kids.add(ref);
    
                // As a result of the reparenting, the effective level
                // may have changed for us and our children.
                updateEffectiveLevel();
    
            }
        }
    
    
    • 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
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    3.4 日常应用

    比如 配置类、日志类、数据库、数据库连接池、线程池

    4.小结和应用场景

    1. 单例模式保证了系统内存中该类只存在一一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能
    2. 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new
    3. 单例模式使用的场景:需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多(即:重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂 等)
  • 相关阅读:
    Stm32_标准库_16_串口&蓝牙模块_手机与蓝牙模块通信_手机传入信息能对芯片时间日期进行更改
    JVM Optimization Learning(四)
    Dubbo实战运用Demo、SpringBoot整合Dubbo、Dubbo中超时重试和负载均衡策略
    基础课4——客服中心管理者面临的挑战
    免疫力好的人,共性已被找到,满足2个以上,应该感到高兴
    Elasticsearch 的页面工具kibana中 dev tool 菜单使用
    我用ChatGPT写了一个简单的Python自动化测试脚本
    前端三刺客---CSS
    Oracle-函数
    【2022 谷歌开发者大会】名额有限,快来,带你体验谷歌的工程师文化
  • 原文地址:https://blog.csdn.net/qq_42264638/article/details/126374575