关于单例模式的感悟

Posted 开发者

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于单例模式的感悟相关的知识,希望对你有一定的参考价值。

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

五、最后


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


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

1321113940


以上是关于关于单例模式的感悟的主要内容,如果未能解决你的问题,请参考以下文章

关于结对编程的体会和感悟

设计模式之单例模式

关于单例模式

关于Javakeywordsynchronized——单例模式的思考

项目整理回顾1,关于单例使用

javascript设计模式阅读后的感悟与总结