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