每天一个设计模式之单例模式(六种写法)
基本介绍
意图
确保系统内某些类至多只有唯一一个实例。
典型例子
- java.lang.Runtime
- java.awt.Desktop
典型应用
- Configuration File
- Cache
- Logger
- drivers
- database connection
UML类图
六种写法
1. Eager模式
// 为何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 返回的对象,而不是反序列化生成的对象,从而防止反序列化破坏单例;
2. Lazy模式
class SingletonLazy {
// 延迟实例化
private static SingletonLazy instance = null;
private SingletonLazy() { }
// 多线程访问会怎样
public static SingletonLazy getInstance() {
if (instance == null)
instance = new SingletonLazy();
return instance;
}
}
缺点
- 线程不安全;
3. 非线程安全的Lazy模式
class SingletonLazySafe {
// 延迟实例化
private static SingletonLazySafe instance = null;
// 有没有什么问题?
public static synchronized SingletonLazySafe getInstance() {
if (instance == null)
instance = new SingletonLazySafe();
return instance;
}
private SingletonLazySafe() { }
}
优点
- 线程安全
缺点
- 即使已经存在实例了,依然需要走同步方法,性能受到影响;
4. 线程安全的Lazy模式
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 关键字有可见性,禁止指令重排的功能。所以距离线程安全就差这一步了;
5. 基于静态内部类实现单例
class SingletonStaticInnerClass {
public static SingletonStaticInnerClass getInstance() {
return SingletonHolder.instance;
}
private static class SingletonHolder {
private static SingletonStaticInnerClass instance = new SingletonStaticInnerClass();
}
private SingletonStaticInnerClass() { }
}
原理
- JVM保证只有在调用getInstance的时候才会去加载内部类且初始化单例;
6. 基于Enum实现单例
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.
设计模式系列博文导航
一、创建型 - 5种
原型模式(Prototype Pattern)
抽象工厂模式(Abstract Factory Pattern)
建造者模式(Builder Pattern)
工厂模式(Factory Pattern)
单例模式(Singleton Pattern)
助记语:原抽建工单
二、结构型 - 8种
享元模式(Flyweight Pattern)
代理模式(Proxy Pattern)
适配器模式(Adapter Pattern)
外观模式(Facade Pattern)
过滤器模式(Filter/Criteria Pattern)
桥接模式(Bridge Pattern)
组合模式(Composite Pattern)
装饰器模式(Decorator Pattern)
助记语:想呆室外,过桥组装
三、行为型 - 11种
责任链模式(Chain of Responsibility Pattern)
命令模式(Command Pattern)
解释器模式(Interpreter Pattern)
中介者模式(Mediator Pattern)
迭代器模式(Iterator Pattern)
观察者模式(Observer Pattern)
策略模式(Strategy Pattern)
状态模式(State Pattern)
备忘录模式(Memento Pattern)
模板方法模式(Template Pattern)
访问者模式(Visitor Pattern)
助记语:责令解中谍,观测状被模仿