每天一个设计模式之单例模式(六种写法)

发布于:2023-01-26 ⋅ 阅读:(18) ⋅ 点赞:(0) ⋅ 评论:(0)

在这里插入图片描述

基本介绍

意图

确保系统内某些类至多只有唯一一个实例。

典型例子

  1. java.lang.Runtime
  2. java.awt.Desktop

典型应用

  1. Configuration File
  2. Cache
  3. Logger
  4. drivers
  5. 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;
    }
}

优点

  1. 简单
  2. 线程安全

缺点

  1. 类加载时就实例化,性能受到影响;

补充

  1. 如何保证线程安全?静态成员变量,在类初始化的时候创建,由jvm保证线程安全;
  2. final 的作用?防止子类覆盖父类方法破坏单例;
  3. 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;
    }
}

缺点

  1. 线程不安全;

3. 非线程安全的Lazy模式

class SingletonLazySafe {
    // 延迟实例化
    private static SingletonLazySafe instance = null;
    // 有没有什么问题?
    public static synchronized SingletonLazySafe getInstance() {
        if (instance == null)
            instance = new SingletonLazySafe();
        return instance;
    }

    private SingletonLazySafe() { }
}

优点

  1. 线程安全

缺点

  1. 即使已经存在实例了,依然需要走同步方法,性能受到影响;

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() { }
}
  1. volatile 的作用
    防止 SingletonDoubleCheck 在实例化的时候指令重排,导致别的线程获得实例的时候虽不为空,但还未赋值;
  2. 为何要做第二次检查?如果没有第二次检查,如果线程1暂停在 synchronized 外面,线程2在 new SingletonDoubleCheck,那么线程2出去之后,线程1进来,它就又new了一次;
  3. 但是两次检查就安全了吗?还不!因为 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() { }
}

原理

  1. JVM保证只有在调用getInstance的时候才会去加载内部类且初始化单例;

6. 基于Enum实现单例

enum  SingletonInEnum {
    INSTANCE;

    public void doSomething() {
        
    }
}

参考:

  1. https://blog.csdn.net/MpenggegeM/article/details/123415319
  2. https://dzone.com/articles/java-singletons-using-enum
  3. https://www.java67.com/2020/05/5-ways-to-implement-singleton-design.html
  4. 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)

助记语:责令解中谍,观测状被模仿


网站公告

欢迎关注微信公众号

今日签到

点亮在社区的每一天
签到