这是我参与11月更文挑战的第30天,活动详情查看:2021最后一次更文挑战
单例模式
单例模式是一个软件的设计模式,为了保证一个类,无论调用多少次产生的实例对象,都是指向同一个内存地址,仅仅只有一个实例(只有一个对象)。
单例模式的优点
1、由于单例模式要求在全局内只有一个实例,因而可以节省比较多的内存空间;
2、全局只有一个接入点,可以更好地进行数据同步控制,避免多重占用;
3、单例可长驻内存,减少系统开销。
单例模式的缺点
1、单例模式的扩展是比较困难的;
2、赋于了单例以太多的职责,某种程度上违反单一职责原则;
3、单例模式是并发协作软件模块中需要最先完成的,因而其不利于测试;
4、单例模式在某种情况下会导致“资源瓶颈”。
单例模式的应用举例
1、生成全局惟一的序列号;
2、访问全局复用的惟一资源,如磁盘、总线等;
3、单个对象占用的资源过多,如数据库等;
4、系统全局统一管理,如Windows下的Task Manager;
5、网站计数器。
设计模式
设计模式是面对各种问题进行提炼和抽象而形成的解决方案。这些设计方案是前人不断试验,考虑了封装性、复用性、效率、可修改、可移植等各种因素的高度总结。它不限于一种特定的语言,它是一种解决问题的思想和方法
补充:常见设计模式,设计模式也衍生出了很多的新的种类,不局限于这23种
设计模式可以分为三个大类:创建类设计模式、结构类设计模式、行为类设计模式
创建类设计模式(5种)
单例模式、工厂模式(简单工厂模式、抽象工厂模式)、建造者模式、原型模式
结构类设计模式(7种)
代理模式、装饰器模式、适配器模式、门面模式、组合模式、享元模式、桥梁模式
行为类设计模式(11种)
策略模式、责任链模式、命令模式、中介者模式、模板模式、迭代器模式、访问者模式、观察者模式、解释器模式、备忘录模式、状态模式
实现单例模式
实现单例模式的手段有很多种,但总的原则是保证一个类只要实例化一个对象,下一次再实例的时候就直接返回这个对象,不再做实例化的操作。所以这里面的关键一点就是,如何判断这个类是否实例化过一个对象。
这里介绍两类方式:
- 一类是通过模块导入的方式;
- 一类是通过魔法方法判断的方式;
1 | python复制代码# 基本原理: |
下面分别介绍这几种不同的实现方式,仅供参考实现思路,不做具体需求。
通过模块导入
1 | python复制代码# cls_singleton.py |
通过类的绑定方法
1 | python复制代码class Student: |
补充:这种方式实现的单例模式有一个明显的bug;bug的根源在于如果用户不通过绑定类的方法实例化对象,而是直接通过类名加括号实例化对象,那这样不再是单利模式了。
通过魔法方法__new__
1 | python复制代码class Student: |
补充:这种方式可以近乎完美地实现单例模式,但是依然不够完美。不完美的地方在于没有考虑到并发的极端情况下,有可能多个线程同一时刻实例化对象。关于这一点的补充内容在本文的最后一节介绍(!!!进阶必会)。
通过元类
1 | python复制代码class Mymeta(type): |
函数装饰器
1 | python复制代码def singleton(cls): |
类装饰器
1 | python复制代码class SingleTon: |
!!!进阶必会
本部分主要是补充介绍多线程并发情况下,多线程高并发时,如果同时有多个线程同一时刻(极端条件下)事例化对象,那么就会出现多个对象,这就不再是单例模式了。
解决这个多线程并发带来的竞争问题,第一个想到的是加互斥锁,于是我们就用互斥锁的原理来解决这个问题。
解决的关键点,无非就是将具体示例化操作的部分加一把锁,这样同时来的多个线程就需要排队。
这样一来只有第一个抢到锁的线程实例化一个对象并保存在_instance
中,同一时刻抢锁的其他线程再抢到锁后,不会进入这个判断if not cls._instance
,直接把保存在_instance
的对象返回了。这样就实现了多线程下的单例模式。
此时还有一个问题需要解决,后面所有再事例对象时都需要再次抢锁,这会大大降低执行效率。解决这个问题也很简单,直接在抢锁前,判断下是否有单例对象了,如果有就不再往下抢锁了(代码第11行判断存在的意义)。
1 | python复制代码import threading |
结语
文章首发于微信公众号程序媛小庄,同步于掘金。
码字不易,转载请说明出处,走过路过的小伙伴们伸出可爱的小指头点个赞再走吧(╹▽╹)
本文转载自: 掘金