43 | 单例模式(下):如何设计实现一个集群环境下的分布式单例模式?
王争
该思维导图由 AI 生成,仅供参考
上两节课中,我们针对单例模式,讲解了单例的应用场景、几种常见的代码实现和存在的问题,并粗略给出了替换单例模式的方法,比如工厂模式、IOC 容器。今天,我们再进一步扩展延伸一下,一块讨论一下下面这几个问题:
如何理解单例模式中的唯一性?
如何实现线程唯一的单例?
如何实现集群环境下的单例?
如何实现一个多例模式?
今天的内容稍微有点“烧脑”,希望你在看的过程中多思考一下。话不多说,让我们正式开始今天的学习吧!
如何理解单例模式中的唯一性?
首先,我们重新看一下单例的定义:“一个类只允许创建唯一一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例设计模式,简称单例模式。”
定义中提到,“一个类只允许创建唯一一个对象”。那对象的唯一性的作用范围是什么呢?是指线程内只允许创建一个对象,还是指进程内只允许创建一个对象?答案是后者,也就是说,单例模式创建的对象是进程唯一的。这里有点不好理解,我来详细地解释一下。
我们编写的代码,通过编译、链接,组织在一起,就构成了一个操作系统可以执行的文件,也就是我们平时所说的“可执行文件”(比如 Windows 下的 exe 文件)。可执行文件实际上就是代码被翻译成操作系统可理解的一组指令,你完全可以简单地理解为就是代码本身。
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
- 深入了解
- 翻译
- 解释
- 总结
本文深入探讨了单例模式在集群环境下的设计与实现。首先强调了单例模式的唯一性范围,包括进程内唯一和线程内唯一。文章介绍了线程唯一单例的实现方式,使用HashMap和ThreadLocal来实现线程内唯一的单例对象。在集群环境下,需要将单例对象序列化并存储到外部共享存储区,并使用分布式锁来保证进程间只有一份对象存在。此外,还介绍了多例模式的实现方式,包括限制数量的多例和根据类型创建多例对象的方式。这些内容对于在集群环境下设计分布式系统的开发人员具有重要参考价值。
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《设计模式之美》,新⼈⾸单¥98
《设计模式之美》,新⼈⾸单¥98
立即购买
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
登录 后留言
全部留言(83)
- 最新
- 精选
- 郭强老师 能讲一讲 线程唯一和集群环境下唯一的单例的实际应用场景吗。光知道如何实现,不知道所谓何用,就变成了屠龙术了。
作者回复: 没啥用的,我就是引申讲一下,训练一下逻辑思维能力
2020-05-1526 - 静心序列化方式实现单例似乎有问题,因为序列化的一般只能序列化对象的属性,对于方法的实例化,除非使用字节码。另外,如果一个类如果没有属性只有方法呢?
作者回复: 没必要序列方法的。
2020-08-0521 - 郑大钱基于线程的单例暂时先不考虑了,多线程编程对我来说酷似洪水猛兽,不好掌控,更别提基于线程的单例了。 进程间的单例直接被iOS的沙盒机制封杀,不要想了。 app开发用得最多的就是经典的单例,多例可能会用得到,mark。
作者回复: ������
2020-11-16 - 神佑小鹿为了保证任何时刻,在进程间都只有一份对象存在,一个进程在获取到对象之后,需要对对象加锁,避免其他进程再将其获取。在进程使用完这个对象之后,还需要显式地将对象从内存中删除,并且释放对对象的加锁。 这个加锁是怎么实现的??
作者回复: 进程间锁 百度了解下
2020-07-292 - 『至爱闫』多个进程为啥能用synchronized加锁。?不要误导人吖
作者回复: 请具体说说,我哪里误导人了?
2020-06-294 - 小晏子要回答这个课后问题,要理解classloader和JDK8中使用的双亲委派模型。 classloader有两个作用:1. 用于将class文件加载到JVM中;2. 确认每个类应该由哪个类加载器加载,并且也用于判断JVM运行时的两个类是否相等。 双亲委派模型的原理是当一个类加载器接收到类加载请求时,首先会请求其父类加载器加载,每一层都是如此,当父类加载器无法找到这个类时(根据类的全限定名称),子类加载器才会尝试自己去加载。 所以双亲委派模型解决了类重复加载的问题, 比如可以试想没有双亲委派模型时,如果用户自己写了一个全限定名为java.lang.Object的类,并用自己的类加载器去加载,同时BootstrapClassLoader加载了rt.jar包中的JDK本身的java.lang.Object,这样内存中就存在两份Object类了,此时就会出现很多问题,例如根据全限定名无法定位到具体的类。有了双亲委派模型后,所有的类加载操作都会优先委派给父类加载器,这样一来,即使用户自定义了一个java.lang.Object,但由于BootstrapClassLoader已经检测到自己加载了这个类,用户自定义的类加载器就不会再重复加载了。所以,双亲委派模型能够保证类在内存中的唯一性。 联系到课后的问题,所以用户定义了单例类,这样JDK使用双亲委派模型加载一次之后就不会重复加载了,保证了单例类的进程内的唯一性,也可以认为是classloader内的唯一性。当然,如果没有双亲委派模型,那么多个classloader就会有多个实例,无法保证唯一性。2020-02-1021294
- 下雨天课堂讨论 Java中,两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等。 单例类对象的唯一性前提也必须保证该类被同一个类加载器加载!2020-02-10858
- DarrenJDK8中是双亲委派制,其实是单亲,parent只有一个,只不过国内翻译的时候翻译成双亲而已,应用类加载器(application class loader)的parent是扩展类加载器(extension class loader),扩展类加载器的parent是启动类加载器(bootstrap class loader)。启动类加载器是由 C++ 实现的,没有对应的 Java 对象,因此在 Java 中只能用 null 来指代。而且这里的类加载器并不是继承的关系,而是组合的的关系。 JDK 9 为了模块化的支持,对双亲委派模式做了一些改动:扩展类加载器被平台类加载器(Platform ClassLoader)取代。平台类加载器和应用程序类加载器都不再继承自 java.net.URLClassLoader,而是继承于 jdk.internal.loader.BuiltinClassLoader。 https://docs.oracle.com/javase/9/migrate/toc.htm#JSMIG-GUID-A868D0B9-026F-4D46-B979-901834343F9E2020-04-10729
- ,深入理解JAVA虚拟机第三版 总结: 大前提:每一个类加载器,都有一个独立的类名称空间(通俗的解释:两个类只有在同一个类加载器加载的前提下,才能比较它们是否"相等") 启动类加载器:加载JAVA_HOME\lib目录下的类库 ↑ 扩展类加载器:加载JAVA_HOME\lib\ext目录下的类库,是java SE 扩展功能, jdk9 被模块化的天然扩展能力所取代 ↑ 应用程序加载器:加载用户的应用程序 ↑ 用户自定义的加载器:供用户扩展使用,加载用户想要的内容 这个类加载器的层次关系被称为类的"双亲委派模型" 双亲委派模型工作流程: 如果一个类加载器收到了加载请求,那么他会把这个请求委派给父类去完成,每一层都是如此,所以他最后会被委派到启动类加载器中,只有父类反馈自己无法完成这个加载请求时,子类才会尝试自己去加载 类不会重复的原因: 比如一个类,java.lang.Object,存放在JAVA_HOME/lib/rt.jar中,无论哪个类加载器想要加载他,最终都会被委派给启动类加载器去加载 反之,如果没有双亲委派机制,用户自己编写一个java.lang.Object类,那么如果他被其他类加载器加载,内存中就会出现两个ava.lang.Object类2020-02-17319
- 李小四设计模式_43: # 作业 Java的类加载有一个双亲委托的机制(递归地让父加载器在cache中寻找,如果都找不到才会让当前加载器去加载),这个机制保证了有诸多好处,与今天的内容相关的就是:不管类名是否相同,不同加载器,加载的一定是不同的类。 1. 如果两个加载器是父子关系,那么只会被加载一次。 2. 如果两个加载器无父子关系,即使加载类名相同的类也会按照不同的类处理。 综上,Java的单例对象对象是类加载器唯一的。 # 感想 今天的内容,有一个感想: 程序员的头脑中,要能够想象程序运行的过程中,内存中发生了什么,我们要对底层多一些研究,否则真的不知其所以然。2020-02-2216
收起评论