设计模式-创建型模式-单例模式

0 引言

创建型模式(Creational Pattern)关注对象的创建过程,是一类最常用的设计模式,每个创建型模式都通过采用不同的解决方案来回答3个问题:创建什么(What),由谁创建(Who)和何时创建(When)。

1 单例模式

单例模式有3个要点:①某个类只能有一个实例;②它必须自行创建这个实例;③它必须自行向整个系统提供这个实例。

单例模式(Singleton),保证一个类仅有一个实例,并提供一个访问它的全局访问点。[DP]

1.1 饿汉单例模式

在类加载的时候就已经实例化,并且创建单例对象,以后直接使用即可。这种模式下,类加载较慢,但获取对象的速度快,且线程安全。

复制代码
public class HungrySingleton {
    // 在类加载时就已经完成了实例的初始化
    private static final HungrySingleton instance = new HungrySingleton();

    // 构造器私有,防止外部通过new关键字创建对象
    private HungrySingleton() {}

    // 提供全局访问点
    public static HungrySingleton getInstance() {
        return instance;
    }

    // 如果需要,可以添加其他方法或属性
    public void showMessage() {
        System.out.println("This is an instance of HungrySingleton.");
    }

    public static void main(String[] args) {
        // 获取单例对象
        HungrySingleton instance1 = HungrySingleton.getInstance();
        HungrySingleton instance2 = HungrySingleton.getInstance();

        // 输出实例,验证是否为同一个对象
        System.out.println(instance1);
        System.out.println(instance2);

        // 验证是否为同一个对象的引用
        System.out.println(instance1 == instance2);

        // 调用实例方法
        instance1.showMessage();
    }
}

1.2 懒汉单例模式

一开始不会实例化,什么时候用就什么时候进行实例化。这种模式下,类加载较快,但获取对象的速度稍慢,且可能在多线程情况下出现线程安全问题。

存在线程安全问题,

复制代码
public class LazySingleton {
    // 私有静态实例,初始化为null
    private static LazySingleton instance = null;

    // 私有构造方法,防止外部通过new关键字创建对象
    private LazySingleton() {}

    // 提供全局访问点
    public static  LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }

    // 如果需要,可以添加其他方法或属性
    public void showMessage() {
        System.out.println("This is an instance of LazySingleton.");
    }

    public static void main(String[] args) {
        // 获取单例对象
        LazySingleton instance1 = LazySingleton.getInstance();

        // 调用实例方法
        instance1.showMessage();
    }
}

加锁,

复制代码
public class LazySingleton {
    // 私有静态实例,初始化为null
    private static LazySingleton instance = null;

    // 私有构造方法,防止外部通过new关键字创建对象
    private LazySingleton() {}

    // 同步方法,提供全局访问点
    public static synchronized LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }

    // 如果需要,可以添加其他方法或属性
    public void showMessage() {
        System.out.println("This is an instance of LazySingleton.");
    }

    public static void main(String[] args) {
        // 获取单例对象
        LazySingleton instance1 = LazySingleton.getInstance();
        LazySingleton instance2 = LazySingleton.getInstance();

        // 输出实例,验证是否为同一个对象
        System.out.println(instance1);
        System.out.println(instance2);

        // 验证是否为同一个对象的引用
        System.out.println(instance1 == instance2);

        // 调用实例方法
        instance1.showMessage();
    }
}

然而,同步方法会导致性能下降,因为每次调用getInstance()方法时都需要进行同步。为了解决这个问题,可以使用**双重校验锁(Double-Checked Locking,DCL)**来实现更高效的懒汉单例模式:现在这样,我们不用让线程每次都加锁,而只是在实例未被创建的时候再加锁处理。同时也能保证多线程的安全。这种做法被称为Double-Check Locking(双重锁定)。

复制代码
public class LazySingletonWithDCL {
    // volatile关键字确保instance在多线程环境下被正确初始化
    private static volatile LazySingletonWithDCL instance = null;

    // 私有构造方法,防止外部通过new关键字创建对象
    private LazySingletonWithDCL() {}

    // 提供全局访问点
    public static LazySingletonWithDCL getInstance() {
        if (instance == null) {
            // 第一次检查
            synchronized (LazySingletonWithDCL.class) {
                if (instance == null) {
                    // 第二次检查
                    instance = new LazySingletonWithDCL();
                }
            }
        }
        return instance;
    }

    // 如果需要,可以添加其他方法或属性
    public void showMessage() {
        System.out.println("This is an instance of LazySingletonWithDCL.");
    }

    public static void main(String[] args) {
        // 获取单例对象
        LazySingletonWithDCL instance1 = LazySingletonWithDCL.getInstance();
        LazySingletonWithDCL instance2 = LazySingletonWithDCL.getInstance();

        // 输出实例,验证是否为同一个对象
        System.out.println(instance1);
        System.out.println(instance2);

        // 验证是否为同一个对象的引用
        System.out.println(instance1 == instance2);

        // 调用实例方法
        instance1.showMessage();
    }
}

使用内部静态类来实现单例模式,这种方式的特点是利用了类加载机制来保证初始化实例时只有一个实例被创建,并且由于JVM的类加载机制,这种方式是线程安全的。只适合java。

复制代码
public class Singleton {
    // 私有构造方法,防止外部通过new关键字创建对象
    private Singleton() {}

    // 静态内部类,持有单例对象
    private static class SingletonHolder {
        // 静态初始化器,由JVM保证线程安全
        private static final Singleton INSTANCE = new Singleton();
    }

    // 提供全局访问点
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }

    // 如果需要,可以添加其他方法或属性
    public void showMessage() {
        System.out.println("This is an instance of Singleton.");
    }

    public static void main(String[] args) {
        // 获取单例对象
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();

        // 输出实例,验证是否为同一个对象
        System.out.println(instance1);
        System.out.println(instance2);

        // 验证是否为同一个对象的引用
        System.out.println(instance1 == instance2);

        // 调用实例方法
        instance1.showMessage();
    }
}
相关推荐
大鹏dapeng2 小时前
使用gone v2 的 Provider 机制升级改造 goner/xorm 的过程记录
后端·设计模式·go
快乐源泉2 小时前
【设计模式】参数校验逻辑复杂,代码长?用责任链
后端·设计模式·go
天堂的恶魔9463 小时前
C++项目 —— 基于多设计模式下的同步&异步日志系统(2)(工厂模式)
开发语言·c++·设计模式
小猪乔治爱打球3 小时前
[Golang修仙之路] 策略模式
设计模式
chushiyunen4 小时前
设计模式-观察者模式和发布订阅模式区别
观察者模式·设计模式
浅陌sss4 小时前
设计模式 --- 观察者模式
设计模式
程序员JerrySUN4 小时前
设计模式 Day 9:命令模式(Command Pattern)完整讲解与实战应用
设计模式·命令模式
小马爱打代码14 小时前
设计模式:依赖倒转原则 - 依赖抽象,解耦具体实现
设计模式
Koma-forever15 小时前
java设计模式-适配器模式
java·设计模式·适配器模式
自在如风。17 小时前
Java 设计模式:原型模式详解
java·设计模式·原型模式