关于单例模式的感悟

开发者(KaiFaX)

我们都是开发者,专注于前端、后端、大数据、区块链、人工智能的知识社区

一、引言

本篇文章是源于工作中遇到了一个与单例模式相关的问题。并由此对单例模式作一个复习和总结。

二、背景

在一次开发过程中,控制台抛出了一个关于写入数据库时,主键冲突的异常。最后排查问题后主要是因为以下三点:
1)获取数据库连接的方式并不是单例的;
2)加载页面时同时请求了相同的接口两次;
3)主键采用的是自动增长。
当然这里若是需要快速解决bug,只需解决问题2,既加载页面时只请求一次该接口。但是问题1才是潜藏更深的隐患。因为较复杂的业务将会占用过多的多余连接,影响系统性能,并可能再次导致上述问题。

三、总结

1、什么是单例模式?

在了解单例模式之前,我们应该先要明白两个问题,什么叫设计模式,设计模式与单例模式又是怎样的关系。

设计模式是面向对象的软件开发员,通过漫长的试验和错误总结出来的一套解决一般问题的方案。
而单例模式,就是其中一种针对一类问题的解决方案。

这就好比设计模式是独孤九剑,而单例模式和其它不同的设计模式就好比破剑式、破刀式。

破剑式就是破解普天下各门各派剑法的招式。那么单例模式又是解决什么问题的呢?并且是如何解决的呢?

单例模式提供了一种最佳的创建对象的方式:
一个单一的类,负责创建自己的对象,同时确保只创建一个对象,并且提供了唯一一个访问此对象的方法,且不需要再实例化该对象。

2、使用场景

现在已经明白单例模式使用的要点既,一个类仅一个实例,并且提供访问对象唯一全局方法。那么单例模式主要解决的是什么呢?一个全局的类频繁的创建和消耗。如何解决的呢?判断是否已有其单例,有则返回,没有则创建。何时使用呢?当我们想控制实例化数量,节省系统资源时。现实场景:1)计划生育,一对夫妻只有一个孩子;2)windows操作一个文件时,多次打开,始终只有这一个文件。抽象到程序中的使用场景:1)生成唯一序列号;2)页面上的计数器;3)创建对象需要消耗比较多的资源,比如IO和数据库的连接。

3、实现方式

这里并不会详细记录其实现步骤(详细步骤可以参考第四大点中的链接),此处仅记录实现单例模式的几种具体方法与各自优劣。
3.1、懒汉式(线程不安全)
示例:public class Single4Lazy {    private static Single4Lazy instance;    private Single4Lazy() {
    }    public static Single4Lazy getInstance() {        if(instance == null) {
            instance = new Single4Lazy();
        }        return instance;
    }
}

优点:实现简单,懒加载。

缺点:线程不安全(没有加锁),严格来说并不能算单例模式。
3.2、懒汉式(线程安全)
示例:public class Single4LazySynchronized {    private static Single4LazySynchronized instance;    private Single4LazySynchronized() {
    }    public static synchronized Single4LazySynchronized getInstance() {        if (instance == null) {
            instance = new Single4LazySynchronized();
        }        return instance;
    }
}

优点:实现简单,线程安全(需要加锁),懒加载。

缺点:效率低(getInstance()方法不能频繁使用,否则影响效率)。
3.3、饿汉式
示例:public class SingleObject {    /**
     * 创建一个私有的单例对象
     */
    private static SingleObject instance = new SingleObject();    /**
     * 构造方法设为私有,这样就不会实例化对象
     */
    private SingleObject() {
    }    /**
     * 获取唯一可用对象
     */
    public static SingleObject getInstance(){        return instance;
    }    public void showMessage() {
        System.out.println("Hello, World ! ");
    }
    
优点:线程安全,实现简单,效率较高(不用加锁)。

缺点:非懒加载,容易生成垃圾对象。
3.4、双检锁(DCL, double-checked locking)
示例:public class Single4DCL {    private static Single4DCL instance;    private Single4DCL() {
    }    public static Single4DCL getInstance() {        if (instance == null) {            /**
             * getInstance()是静态方法,所以不能使用未静态或未实例的类对象
             */
            synchronized (Single4DCL.class) {                if (instance == null) {
                    instance = new Single4DCL();
                }
            }
        }        return instance;
    }
}

优点:线程安全,懒加载,效率较高。

缺点:JDK1.5起,实现较复杂,getInstance()性能对应用程序很关键。
3.5、静态内部类
示例:public class Single4Static {

    private static class Single4StaticHolder {
        private static final Single4Static INSTANCE = new Single4Static();
    }    private Single4Static() {
    }    public static Single4Static getInstance() {        return Single4StaticHolder.INSTANCE;
    }

}

优点:线程安全,效率较高,懒加载,实现难度一般。

缺点:只适用于静态域情况。
3.6、枚举
示例:public enum Single4Enum {

    INSTANCE;    public void whateverMethod() {

    }
    
}

优点:线程安全,最佳实现,自动支持序列化机制,绝对防止多次实例化,不能通过reflection attack来调用私有构造方法

缺点:JDK1.5起

4、小tips

构造器是私有的。
通常不使用第1、2种,一般使用第3种,只有明确需要懒加载时使用第5种,
需要反序列化创建时可以尝试第6种,有其它特殊需求时,可以考虑第4种。

四、参考


https://www.runoob.com/design-pattern/singleton-pattern.html

https://www.runoob.com/design-pattern/design-pattern-tutorial.html

https://baike.baidu.com/item/%E7%8B%AC%E5%AD%A4%E4%B9%9D%E5%89%91/51022?fr=aladdin

五、最后

若有不足,欢迎指正。
求知若渴,虚心若愚

作者:MO_or

原文:https://segmentfault.com/a/1190000022183819

说明:我们都是开发者。视频或文章来源于网络,如涉及版权或有误,请您与若飞(1321113940)联系,将在第一时间删除或者修改,谢谢!


1. 回复“m”可以查看历史记录;

2. 回复“h”或者“帮助”,查看帮助;

  开发者已开通多个微信群交流学习,请加若飞微信:1321113940 进群学习交流

开 发 者 : KaiFaX

我们都是开发者

专注于前端、后端、大数据、区块链、人工智能的知识社区

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值