单例模式及其思想

本文包括以下几点↓

结论设计模式不是简单地将一个固定的代码框架套用到项目中,而是一种严谨的编程思想,旨在提供解决特定问题的经验和指导。

单例模式(Singleton Pattern)

意图

旨在确保类只有一个实例,并提供一个全局访问点以访问该实例。

适用性

当你希望系统中只有一个实例,并且需要从全局任何地方都能访问该实例时。

当你需要严格控制某个类的实例化过程,以确保所有代码都使用相同的实例时。

简述

单例模式的核心思想是控制对象只实例化一次,并提供一个全局访问点来获取该实例。这样可以确保在系统中只有一个实例,并且所有对该实例的访问都通过同一个访问点。

实现单例模式有几种方式,通过以下案例展开。

简单案例

六种常见的单例模式实现方式,逐一分析它们的特点和适用场景:

  1. 懒汉式之线程不安全:

源码

java 复制代码
public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  

    public static Singleton getInstance() {  
        if (instance == null) {  
            instance = new Singleton();  
        }  
        return instance;  
    }  
}

特点: 懒加载,在首次使用时创建实例化对象。在多线程环境下,可能会出现并发问题,导致创建多个实例。

适用场景: 单线程环境下的简单应用,对性能要求不高,且不会频繁使用单例的场景。

  1. 懒汉式之线程安全

源码

复制代码
public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
    public static synchronized Singleton getInstance() {  
        if (instance == null) {  
            instance = new Singleton();  
        }  
        return instance;  
    }  
}

特点: 同样是懒加载,但使用了 synchronized 关键字来保证线程安全。然而,这会造成性能下降,因为每次获取实例对象都需要加锁。

适用场景: 在多线程环境下使用,对性能要求不高的场景,或者对并发要求不是特别高的情况。

  1. 饿汉式

源码

复制代码
public class Singleton {  
    private static Singleton instance = new Singleton();  
    private Singleton (){}  
    public static Singleton getInstance() {  
        return instance;  
    }  
}

特点: 在类加载时就创建实例化对象,因此不存在多线程安全问题,且访问时效率高。但可能会浪费内存,因为实例在程序启动时就创建了,即使未被使用也会占用内存。

适用场景: 对内存占用没有过多要求,且希望在程序启动时就初始化单例的场景。

  1. 双检锁/双重校验锁

源码

复制代码
public class Singleton {  
    private volatile static Singleton singleton;
    private Singleton (){}
    public static Singleton getSingleton() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

特点: 通过双重检查加锁,既实现了懒加载,又保证了线程安全,且在实例已创建的情况下避免了不必要的锁竞争,因此性能较高。

适用场景: 对性能要求较高,且需要懒加载的场景,特别是在多线程环境下。

  1. 登记式/静态内部类

源码

复制代码
public class Singleton {
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    private Singleton (){}
    public static final Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

特点: 通过静态内部类实现了懒加载,且保证了线程安全。在类加载时并不会初始化,只有在首次调用 getInstance() 方法时才会初始化。

适用场景: 对内存占用有要求,且希望在首次使用时才初始化的场景。

  1. 枚举类

源码

复制代码
public enum Singleton {
    INSTANCE;
//  private Singleton(){}
    public Singleton getSelf() {
        return INSTANCE;
    }
}
public static void main(String[] args) {
    Singleton instance = Singleton.INSTANCE;
}

特点: 使用枚举类型实现单例,利用枚举的特性保证了在任何情况下都只会有一个实例存在。枚举类的实例在类加载时就会被初始化。

适用场景: 对内存占用没有过多要求,且希望简洁明了地实现单例的场景。枚举类也是线程安全的。

枚举类本身就是单例模式Singleton instance = Singleton.INSTANCE; 并且可以有多个枚举INSTANCE,TWO;枚举类中的INSTANCE就是Singleton对象实例,看getSelf方法不报错就知道了;枚举类的构造方法本身就是private,放开实例中的注释结果一样;枚举中的方法只能通过Singleton.INSTANCE.getSelf()的方式来调用,这就是单例的表现。

这是实现单例模式的最佳方法,它更简洁,自动支持序列化机制,绝对防止多次实例化。但不能通过反射来调用私有构造方法。

在Spring框架中的应用??

在Spring框架中,使用到单例模式思想的地方非常多,被广泛应用于管理和创建bean实例。Spring容器默认情况下会将所有的bean都配置为单例,这意味着在整个应用程序中,每个bean都只有一个实例存在。这种默认的单例模式确保了Spring应用程序的性能和资源利用率。

在Spring上下文中,通过使用@Bean@Component@Service@Repository 等注解或在配置文件中显式声明bean,Spring会自动将其配置为单例模式,即在整个应用程序中只创建一个实例。比如配置类的创建。

例如:数据库连接配置类

java 复制代码
@Configuration
public class AppConfig {

    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/mydatabase");
        dataSource.setUsername("username");
        dataSource.setPassword("password");
        return dataSource;
    }
}

在Spring框架中的这个单例模式是使用单例设计模式创建Bean的吗?

很显然不是。最显著的一点就是,你在项目中创建的类的构造方法是私有的吗?

Spring 底层创建单例模式的Bean 使用的是BeanFactory工厂设计模式去创建的,在此别混淆了。这种Bean概念更准确的描述应该是Bean的单例作用域

除此之外,Spring框架还提供了其他类型的作用域,比如Prototype原型模式,可以通过 @Scope("prototype") 注解来标注一个bean,这样每次从容器中获取该bean时,容器都会创建一个新的实例。

复制代码
@Component
@Scope("prototype")
public class MyPrototypeBean {
    // 类的具体代码
}

Prototype原型作用域的Bean对象和原型设计模式有什么关系呢?在原型设计模式中再进行分享。

总结:

单例模式的作用主要包括以下几个方面:

  1. 确保全局唯一实例: 单例模式确保系统中某个类只有一个实例存在,无论在何处进行实例化,都会获得相同的实例。这可以防止多个对象之间的状态不一致或冲突,确保所有使用者操作一致,有助于管理全局资源。
  2. 提供全局访问点: 单例模式提供一个全局访问点,使得任何地方都可以轻松访问该实例。这在需要频繁访问某个对象或共享资源的情况下非常有用,可以简化代码逻辑,提高代码的可读性和可维护性。
  3. 节约系统资源: 由于单例模式只创建一个对象实例,因此可以避免重复创建对象所带来的性能和内存消耗。特别是在频繁访问该对象的场景下,单例模式可以有效减少系统资源的使用。
  4. 控制对象的实例化过程: 单例模式可以对对象的实例化过程进行严格控制,确保只有一个实例存在,并且在运行时动态创建或延迟实例化,以满足特定的需求。这在需要对对象的创建过程进行特殊处理或优化时非常有用。
  5. 避免全局变量的滥用: 单例模式可以将全局状态封装在一个对象中,避免使用大量的全局变量和静态方法,从而提高代码的可维护性和可测试性。

简言之,单例模式在需要确保系统中某个类只有一个实例存在,并且需要提供全局访问点。它可以简化系统架构,提高代码的重用性和可维护性,同时节约系统资源,提高系统性能。但是要注意,线程是否安全的问题。

看了Spring单例作用域的Bean,应该知道一点:

设计模式不是简单地将一个固定的代码框架套用到项目中,而是一种严谨的编程思想,旨在提供解决特定问题的经验和指导。

就单例设计模式来说,控制一个类只有一个实例,并提供一个全局访问点,掌握这个编程思想的应用才是学习设计模式的关键。

软考中级--软件设计师毫无保留的备考分享

2023年下半年软考考试重磅消息

通过软考后却领取不到实体证书?

计算机算法设计与分析(第5版)

Java全栈学习路线、学习资源和面试题一条龙

软考证书=职称证书?

什么是设计模式?

相关推荐
海特伟业15 小时前
隧道调频广播覆盖-隧道调频广播无线覆盖系统建设要点、难点分析与解决应对
运维·设计模式
sg_knight15 小时前
设计模式实战:享元模式(Flyweight)
python·设计模式·享元模式·flyweight
Swift社区18 小时前
AI 时代,ArkUI 的设计模式会改变吗?
人工智能·设计模式
数据中穿行18 小时前
访问者设计模式全方位深度解析
设计模式
宁雨桥19 小时前
前端设计模式面试题大全
前端·设计模式
数据中穿行20 小时前
迭代器设计模式全方位深度解析
设计模式
数据中穿行20 小时前
观察者设计模式全方位深度解析
设计模式
程序员Terry20 小时前
别老写重复代码了!模版方法模式一次讲透
java·设计模式
数据中穿行20 小时前
建造者模式全方位深度解析
设计模式