- 基本介绍
- 意图
- 典型例子
- 典型应用
- UML类图
- 六种写法
- 1. Eager模式
- 优点
- 缺点
- 补充
- 2. Lazy模式
- 缺点
- 3. 非线程安全的Lazy模式
- 优点
- 缺点
- 4. 线程安全的Lazy模式
- 5. 基于静态内部类实现单例
- 原理
- 6. 基于Enum实现单例
- PLUS - 如果本文对你有帮助,可以随手扫个码,领个包再走!![在这里插入图片描述](https://img-blog.csdnimg.cn/d6bb2a99bd72400a938de8d4c374a841.jpeg =300x450)
确保系统内某些类至多只有唯一一个实例。
典型例子- java.lang.Runtime
- java.awt.Desktop
- Configuration File
- Cache
- Logger
- drivers
- database connection
// 为何final class final Singleton extends Serializable { // 一上来就实例化,eagerly;线程安全吗? private static Singleton instance = new Singleton(); private Singleton() { } public static Singleton getInstance() { return instance; } // 这是什么方法,来自谁? private Object readResolve() throws ObjectStreamException { return instance; } }优点
- 简单
- 线程安全
- 类加载时就实例化,性能受到影响;
- 如何保证线程安全?静态成员变量,在类初始化的时候创建,由jvm保证线程安全;
- final 的作用?防止子类覆盖父类方法破坏单例;
- readResolve 的作用?加入readResolve方法,在反序列化时就会采用 readResolve 返回的对象,而不是反序列化生成的对象,从而防止反序列化破坏单例;
class SingletonLazy { // 延迟实例化 private static SingletonLazy instance = null; private SingletonLazy() { } // 多线程访问会怎样 public static SingletonLazy getInstance() { if (instance == null) instance = new SingletonLazy(); return instance; } }缺点
- 线程不安全;
class SingletonLazySafe { // 延迟实例化 private static SingletonLazySafe instance = null; // 有没有什么问题? public static synchronized SingletonLazySafe getInstance() { if (instance == null) instance = new SingletonLazySafe(); return instance; } private SingletonLazySafe() { } }优点
- 线程安全
- 即使已经存在实例了,依然需要走同步方法,性能受到影响;
class SingletonDoubleCheck { // 延迟实例化,volatile 的作用? private static volatile SingletonDoubleCheck instance = null; public static SingletonDoubleCheck getInstance() { if (instance == null) { synchronized(SingletonDoubleCheck.class) { // 这一次检查的意义是? if (instance == null) instance = new SingletonDoubleCheck(); } } return instance; } private SingletonDoubleCheck() { } }
- volatile 的作用
防止 SingletonDoubleCheck 在实例化的时候指令重排,导致别的线程获得实例的时候虽不为空,但还未赋值; - 为何要做第二次检查?如果没有第二次检查,如果线程1暂停在 synchronized 外面,线程2在 new SingletonDoubleCheck,那么线程2出去之后,线程1进来,它就又new了一次;
- 但是两次检查就安全了吗?还不!因为 new SingletonDoubleCheck() 是一个复合操作;
初始化对象的过程:
1) 分配内存
2) 初始化对象
3)设置instance指向刚分配的地址
发生指令重排可能会改变123 的顺序变成132,这时候对象可能还没有初始化完就已经实例化了。
volatile 关键字有可见性,禁止指令重排的功能。所以距离线程安全就差这一步了;
class SingletonStaticInnerClass { public static SingletonStaticInnerClass getInstance() { return SingletonHolder.instance; } private static class SingletonHolder { private static SingletonStaticInnerClass instance = new SingletonStaticInnerClass(); } private SingletonStaticInnerClass() { } }原理
- JVM保证只有在调用getInstance的时候才会去加载内部类且初始化单例;
enum SingletonInEnum { INSTANCE; public void doSomething() { } }
参考:
- https://blog.csdn.net/MpenggegeM/article/details/123415319
- https://dzone.com/articles/java-singletons-using-enum
- https://www.java67.com/2020/05/5-ways-to-implement-singleton-design.html
- https://www.geeksforgeeks.org/singleton-design-pattern-introduction/?ref=rp5.