Singleton
Preface
概念
定义:单例模式确保一个类只有一个实例,并提供一个全局访问点。
唯一的作用范围:
单例模式:进程间不唯一,进程内唯一,线程间唯一。
线程单例:线程内唯一,线程间不唯一。
集群单例:进程间唯一。
使用场景:
解决资源访问冲突。比如日志都往一个文件写,写如文件的类就应该是单例。
表示全局唯一数据,比如配置、线程池、缓存、注册表。
缺点
对 OOP 特性支持不友好,对抽象、继承、多态支持不友好。
会隐藏类之间的依赖关系。
对扩展性不友好。比如连接池若设计为单例,若以后需要增加一个慢 sql 连接池,就需要两个实例了。
对测试性不友好。
不支持有参数的构造函数。
替代方案
静态方法。但是并不能解决上节的缺点。
将单例生成的对象作为参数传递给函数。可以解决第二点缺点。
工厂模式。
IOC 容器。
程序员自己保证不要创建两个对象。
饿汉式
不管是否需要这个对象都会创建。
直接实例化
若该类实现了 Serializable 接口,就可以通过序列化的方式破坏单例模式。可以添加 readResolve() 方法,自定义返回对象的策略。
枚举
最简洁的方式。
静态代码块
适合于初始化比较复杂的情况。
懒汉式
延迟创建对象。
线程不安全
适用于单线程。
线程安全 - 双重检测
适用于多线程。
线程安全 - 静态内部类
适用于多线程。
原理:静态内部类不会自动随着外部类的加载和初始化而初始化。
线程单例
通过一个线程安全的 Map,key 是线程 ID,value 是对象。其实 Java 提供的 ThreadLocal 可以轻松实现线程单例,不过 ThreadLocal 底层也是用 Map 实现的。
集群单例
需要把单例序列化到外部存储,进程在使用这个单例时需要先从外部存储读取到内存,并反序列化为对象,使用完成后再序列化回外部存储。是从外部存储读取、写入时需要加锁。
多例模式
多例指的是一个类可以创建多个对象,但是个数有限,比如 3 个。
这个与工厂模式有点类似,多例模式创建的都是同一个类的对象,工厂模式创建的是不同的子类对象。也类似于享元模式。
枚举也相当于多例模式。
小结
饿汉式都不存在线程安全问题。
饿汉式枚举的方式最简洁,懒汉式静态内部类的方式最简洁。
上述实现存在多个类加载器问题。如果有两个及以上的类加载器都加载了这个类,那么还是会产生多个单件。解决办法是自行指定类加载器,并指定同一个类加载器。
Last updated
Was this helpful?
