第二章:对象创建型模式之单例模式

1.什么是单例模式?

确保一个类在整个应用程序的生命周期中只有一个实例,并提供一个全局访问点来访问该实例。

根据单例模式的定义,单例模式主要具有以下特点:

  1. 一个类只能有一个实例。
  2. 这个类必须自行创建自己的实例。
  3. 它必须向其他所有对象提供这一唯一实例。

2.为什么使用单例模式?

1.一个类需要经常被创建或销毁时,可以使用单例模式减少资源浪费。

2.单例模式可以控制创建实例的数量。

3.单例模式创建的对象可以与其他对象共享状态,确保其他类依赖同一个单例实例

4.可以避免对资源的多重占用。

3.单例模式的常见的应用场景

1.生产唯一序列号。

2.管理web应用的计数器,避免每次请求都重新计算。

3.对于资源消耗较大的类,例如频繁执行I/O操作的类。

4.怎么创建单例模式?

1.创建一个类,并将其构造函数设置为私有的不可调用的。

2.为该类创建一个静态的“getInstance”全局访问方法,来获取该类的实例引用。

3.在getInstance方法中如果单例对象已经存在,则直接返回,如果不存在,则调用实例化方法创建一个新的对象并返回。

4.如果单例对象尚未创建,则在getInstance 方法中使用同步锁‘synchronized’来保护单例对象的实例化过程。

5.常见的单例模式有哪些?

1.懒汉式 - 线程不安全

这种方式的特点是实现简单,可以实现对象懒加载,但这种方式最大的缺点是线程不安全,在多线程情况下不能正常工作。

实现代码如下:

/*<懒汉式-线程不安全>实现代码*/
public class Singleton {
    //1.私有化构造函数,
    // 保证其他类不能通过正常方法实例化单例对象,只能通过单例类自行实例化
    private Singleton() {}
    //保存全局唯一单例对象
    private static Singleton singleton;
    
    //3.通过全局静态访问方法向其他对象提供单例对象
    public static Singleton getInstance() {
        //2.自行实例化单例对象
        if(singleton == null) {// 确保只有一个单例对象(线程不安全)
            singleton = new Singleton();
        }
        return singleton;
    }
}

2.懒汉式 - 线程安全

这种方式的特点是实现简单,能实现对象懒加载,同时兼顾线程安全,缺点是使用synchronized 同步锁,导致频繁获取单例对象时效率低下

实现代码如下:

/*<懒汉式-线程安全>实现代码*/
public class Singleton {
    //1.私有化构造函数,
    // 保证其他类不能通过正常方法实例化单例对象,只能通过单例类自行实例化
    private Singleton() {}
    //保存全局唯一单例对象
    private static Singleton singleton;
    
    //3.通过全局静态访问方法像其他对象提供单例对象
    public static synchronized Singleton getInstance() {
        //2.自行实例化单例对象
        if(singleton == null) {// 确保只有一个单例对象(线程不安全)
            singleton = new Singleton();
        }
        return singleton;
    }
}

3.饿汉式

这种方式的特点是在类加载时就初始化单例对象,同时基于classloader 类加载机制避免了多线程问题,由于没有加锁,效率很高;缺点是对象没有实现懒加载,容易产生垃圾对象

实现代码如下:

/*<饿汉式>实现代码*/
public class Singleton {
    //1.私有化构造函数,
    // 保证其他类不能通过正常方法实例化单例对象,只能通过单例类自行实例化
    private Singleton() {}
    //2.自行实例化单例对象, 并保存全局唯一单例对象
    private static Singleton singleton = new Singleton();
    
    //3.通过全局静态访问方法像其他对象提供单例对象
    public static synchronized Singleton getInstance() {
        return singleton;
    }
}

4.双重锁/双重校验锁

这种方式的特点是通过双重锁实现线程安全并保证高效率,缺点是实现代码略显复杂

实现代码如下:

/*<双重锁/双重校验锁>实现代码*/
public class Singleton {
    //1.私有化构造函数,
    // 保证其他类不能通过正常方法实例化单例对象,只能通过单例类自行实例化
    private Singleton() {}
    //保存全局唯一单例对象
    private volatile static Singleton singleton;
    
    //3.通过全局静态访问方法像其他对象提供单例对象
    public static Singleton getInstance() {
        //2.自行实例化单例对象,并通过双重锁或双重校验锁保证线程安全
        if(singleton == null) {
            synchronized(Singleton.class) { //双重锁
                if(singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

5.登记式/静态内部类

这种方式既通过classloader 类加载机制保证线程安全,有实现了实例对象的懒加载(只有静态内部类实际被使用时才会加载静态内部类,并生成实例对象)

实现代码如下:

/*<登记式/静态内部类>实现代码*/
public class Singleton {
    //1.私有化构造函数,
    // 保证其他类不能通过正常方法实例化单例对象,只能通过单例类自行实例化
    private Singleton() {}
    
    private static class SingletonHolder {
        //2.自行实例化单例对象,保存全局唯一单例对象
        private static final Singleton INSTANCE = new Singleton();
    }
    
    //3.通过全局静态访问方法像其他对象提供单例对象
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

6.枚举

这种方式特点是线程安全,支持序列化机制,可以避免反序列化创建新对象,绝对防止多次实例化。是实现单例模式的最佳方式

实现代码如下:

/*<枚举>实现代码*/
public enum  Singleton {
    INSTANCE;
    public void getConfig() {}

}

总结:

一般情况下不建议使用懒汉式实现单例模式

如果实例化时消耗资源比较少,建议实用饿汉式实现单例模式

如果实例化时消耗资源多且明确需要懒加载单例对象时,才建议实用“登记式/静态内部类”实现单例模式

如果要防止反序列化创建对象时,可以使用枚举实现单例模式

双重锁/双重校验锁实现比较复杂,不建议使用。 

6.哪些框架使用了单例模式?

1.Spring 框架中每个Bean默认都是通过单例进行管理的,每个Bean在Spring IOC 容器中只有一个实例。

2. Spring框架中的ApplicationContext代表了Spring IOC 容器,负责管理Bean的生命周期,它本身就是一个单例,确保整个应用程序中只有一个实例。

7.单例模式是否遵循软件设计原则?

单例模式由于没有接口,不能继承,因此不符合单一职责原则,一个类应该只关注它的内部逻辑,而不应该关注外部类怎么实例化它。

你可能感兴趣的:(设计模式,单例模式,java,spring,boot,spring,设计模式)