设计模式 | 单例模式的四种实现形式

本贴最后更新于 1807 天前,其中的信息可能已经东海扬尘

javaDEMO

Java 基础 Demo 站: https://www.javastudy.cloud
Java 中高级开发博客: https://www.lixiang.red
Java 学习公众号: java 技术大本营
java_subscribe

单例模式总述

所谓单例,就是全局就一个实例,不用 new,拿来即用,常见的有以下四种方式来使用
不管哪种方式,都要把构造函数给私有化,不准用户自己创建

  1. 提前实例化对象,然后直接获取
  2. 在获取的时候进行实例化
  3. 双重加锁式实例化对象
  4. 静态内部类来实例化对象

提前实例化对象(饿汉式)

/**
 * @Author https://www.javastudy.cloud
 * @CreateTime 2019/11/26
 **/
public class Demo {

    /**
     * 使用 static 是为了不用new,可以直接使用getDemo方法
     * 使用 final 是为了别人不能对这个对象更改
     * */
    private static final  Demo DEMO = new Demo();

    /**
     * 无论哪种方式,构造函数都是私有的
     */
    private Demo() {
    }

    /**
     * 外面都使用这个方法来获取DEMO实例
     */
    public static Demo getDemo(){
        return DEMO;
    }

}

获取的时候再去初始化(懒汉式)

/**
 * @Author https://www.javastudy.cloud
 * @CreateTime 2019/11/26
 **/
public class Demo {
    /**
     * 使用 static 是为了不用new,可以直接使用getDemo方法
     *
     * */
    private static  Demo DEMO;

    /**
     * 无论哪种方式,构造函数都是私有的
     */
    private Demo() {
    }

    /**
     * 外面都使用这个方法来获取DEMO实例
     * 先进行判空,为空的话先进行初始化
     * 不为空的话则直接返回
     */
    public static Demo getDemo(){
        if(DEMO == null){
            DEMO = new Demo();
        }
        return DEMO;
    }

}

双层判空(线程安全版)

在上述代码中如线程 A 和线程 B 都执行到 if(DEMO == null )这一行, 然后都得到的为空, 然后就都会 new 一个 Demo 出来,就有两个不同的 DEMO,就不是单例模式了
如此便有了加锁的线程安全的单例模式

/**
 * @Author https://www.javastudy.cloud
 * @CreateTime 2019/11/26
 **/
public class Demo {
    /**
     * 使用 static 是为了不用new,可以直接使用getDemo方法
     *
     * */
    private static  Demo DEMO;

    private static final Object lock = new Object();

    /**
     * 无论哪种方式,构造函数都是私有的
     */
    private Demo() {
    }

    /**
     * 外面都使用这个方法来获取DEMO实例
     * 先进行判空,为空的话先进行加锁,然后再进行初始化的过程
     * 不为空的话则直接返回
     */
    public static Demo getDemo(){
        if(DEMO == null){
            synchronized (lock){
                //这里最为关键,见下方解释
                if(DEMO == null){
                    DEMO = new Demo();
                }
            }
        }
        return DEMO;
    }

}

在锁内的判空中,如 AB 两个线程都在等待锁,A 先进去初始化, 然后退出来返回. B 进去之后,若不再进行判空,会再重新初始化,这样就不是单例了.

静态内部类

加锁是有开销的,因此有了另外一种线程安全的做法,用静态内部类的方式

/**
 * @Author https://www.javastudy.cloud
 * @CreateTime 2019/11/26
 **/
public class Demo {

    private static class InnerSingle{
        private static Demo DEMO  = new Demo();
    }
    

    /**
     * 无论哪种方式,构造函数都是私有的
     */
    private Demo() {
    }

    /**
     * 直接设用内部类获取实例
     */
    public static Demo getDemo(){
        return InnerSingle.DEMO;
    }

}

这种方式即能做到需要时加载,又能做到线程安全.在外部类加载时,不会立即加载内部类,只有调用 getDemo 时, 才会加载内部类,而且只会加载一次. 在实际开发中,也是以这种方式使用为多.

单例模式使用场景

通常来说,我们会用单例模式来做一些配置,client 等。 如以下场景:
1.调用 Http 的 Httpclient,这个每次初始化都会很重量级,只用初始化一个就可以了。
2.全局配置,从配置文件来初始化一个 Configuration,然后全局调用这一个就可以了
3. 一些工具类,通常来说,我们工具类都是用的 static,但是有些工具类时配置和运行分开的,会那单例获取工具类的配置
综上所述,可以看到,单例模式最大的使用场地还是在配置上面和工具上

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3190 引用 • 8214 回帖 • 1 关注
  • 设计模式

    设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

    200 引用 • 120 回帖
  • 单例模式
    8 引用 • 3 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • someone

    这个单例模式一直很懵,这个模式应用在什么地方呢

    1 回复
  • someone

    SpringBoot 中有了 Service 注解之后,这个用的就不多了。

  • someone
    作者

    几种常用的用法已补充在最后一段,感谢支持