【设计模式】单例模式(创建型)⭐⭐⭐

1.概念

1.1 什么是单例模式

单例模式属于创建型模式,一个单例类在任何情况下都只存在一个实例
构造方法必须是私有的、由自己创建一个静态变量存储实例,对外提供一
个静态公有方法获取实例。

1.2 优点与缺点

优点:是内存中只有一个实例,减少了开销,尤其是频繁创建和销毁实例的情况下,可以避免对资源的多重占用。
缺点:没有抽象层,难以扩展,与单一职责原则冲突(单例模式由于其全局访问的特性,往往会使得类的使用变得非常广泛,这会导致类的职责膨胀,变得越来越难以维护。)。

2.实现方式

2.1 懒汉式

懒加载 (lazy loading):使用的时候再创建对象。

2.1.1 懒汉式(线程不安全)

在第一次调用获取实例的方法时才创建实例。由于没有同步措施,因此线程不安全。

public class Singleton {
    private static Singleton instance;

    private Singleton() {}

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

2.1.2 懒汉式(线程安全)

在懒汉式的基础上,通过同步代码块或方法来确保线程安全,但会降低性能。

public class Singleton {
    private static Singleton instance;

    private Singleton() {}

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

2.2 饿汉式

类加载时就创建好实例,避免了线程同步问题,但可能会导致资源浪费。

public class Singleton {
    private static Singleton instance = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return instance;
    }
}

2.3 双重检查锁定(Double-Checked Locking)

结合懒汉式和线程安全的特点,在获取实例时进行双重检查,以提高性能。

public class Singleton {
    private static volatile Singleton instance;

    private Singleton() {}

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

volatile关键字的作用是确保instance变量的可见性和防止指令重排序。

2.4 静态内部类

利用Java的类加载机制来实现懒加载和线程安全。

public class Singleton {
    private Singleton() {}

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

在这个示例中,SingletonHolder是一个静态内部类,它包含了一个静态的Singleton实例。SingletonHolder类只有在getInstance()方法被调用时才会被加载,这保证了Singleton实例的延迟加载。同时,由于类的加载过程是线程安全的,所以这种方式也保证了单例的线程安全。

2.5 枚举

使用枚举来实现单例模式,这是最简单的实现方式,并且天生线程安全,防止多次实例化。

public enum Singleton {
    INSTANCE1,
    INSTANCE2;
    
    public void someMethod() {
        // 功能处理
        System.out.println("执行一些功能处理");
    }
}
public class Main {
    public static void main(String[] args) {
        // 调用第一个枚举实例的方法
        Singleton.INSTANCE1.someMethod();
        
        // 调用第二个枚举实例的方法
        Singleton.INSTANCE2.someMethod();
    }
}

枚举天生线程安全的原因在于Java枚举的设计和实现:
枚举实例的创建时机:枚举的实例是在类加载时创建的,这个过程是由Java虚拟机(JVM)在加载枚举类时自动完成的。JVM确保在任何线程可以访问枚举的实例之前,这些实例已经被创建并初始化完毕
Java内存模型(JMM):Java内存模型确保了所有线程可以看到枚举实例的创建和初始化状态。这意味着在任何线程可以访问枚举的实例之前,这些实例的状态对于所有线程都是可见的
同步机制:在枚举实例的创建过程中,JVM内部会使用同步机制来确保实例的创建是原子性的,也就是说,在创建枚举实例的过程中,不会有其他线程干扰。
不可变性:枚举实例一旦被创建,就不能被修改或重新赋值。这意味着多个线程可以安全地访问枚举实例,而不会出现竞态条件或数据不一致的问题。
由于这些原因,枚举天生就是线程安全的,不需要开发者手动处理同步问题。

3 Java 哪些地方用到了单例模式

资源管理器:如数据库连接池、缓存管理器等,通常需要全局访问点,并且在整个应用生命周期内只需要一个实例。
配置管理器:用于加载和解析配置文件的类,通常设计为单例,以确保配置的全局一致性和访问的便捷性。
日志记录器:如Log4j、SLF4J等日志框架,通常提供单例的日志记录器,以便在整个应用中统一记录日志。
服务定位器(Service Locator):在某些设计模式中,服务定位器用于查找和存储服务对象,通常实现为单例。
打印机 spooler:在操作系统中,打印机管理器通常是单例的,以确保打印任务的有序处理。
线程池:在多线程编程中,线程池通常设计为单例,以避免创建多个线程池的资源浪费。
Web应用中的共享资源:如在Servlet中,一个ServletContextListener可以用来创建单例资源,并在整个Web应用中共享。
应用上下文:在Spring框架中,ApplicationContext本身就是一个单例,用于管理Spring beans。
工厂类:在设计模式中,工厂类通常设计为单例,以确保对象创建的统一性和便捷性。
窗口管理器:在图形用户界面(GUI)应用中,窗口管理器通常是单例的,以管理所有窗口和对话框。
驱动程序管理器:如JDBC的DriverManager类,它负责管理数据库驱动程序,通常是单例的。
应用级的状态管理:如用户会话管理、应用级别的缓存等,通常需要全局访问点,并且只有一个实例。

4 Spring 哪些地方用到了单例模式

Spring Bean的默认作用域:在Spring中,当你定义一个bean时,如果没有指定它的作用域,它默认是单例的。这意味着在整个Spring IoC容器中,这个bean只有一个实例。这是最常见的单例模式使用场景。
Spring的ApplicationContext:Spring的应用上下文(ApplicationContext)自身就是一个单例。在整个应用生命周期内,通常只有一个ApplicationContext实例,它负责管理所有的Spring beans。
Spring的事件发布机制:Spring的事件发布机制使用单例模式来确保事件的多播器(ApplicationEventMulticaster)是单例的。这样可以确保事件在整个应用中被统一管理和分发。
数据源(DataSource):在Spring中配置数据源时,通常会将其配置为单例,以确保整个应用共享同一个数据库连接池。
事务管理器(TransactionManager):事务管理器通常也是单例的,以确保整个应用中的事务可以被统一管理。
Spring Security的SecurityContextHolder:Spring Security使用SecurityContextHolder来持有安全上下文信息(Spring Security提供了一套全面的安全服务,包括认证、授权),它是单例的,可以在整个应用中访问。
缓存管理器(CacheManager):在使用Spring的缓存抽象时,缓存管理器通常是单例的,以确保缓存策略在整个应用中一致。
任务调度器(TaskScheduler):Spring的任务调度器通常也是单例的,以确保定时任务和异步任务的统一调度。
消息服务(MessageService):在使用Spring集成或Spring Boot与消息中间件集成时,消息服务通常是单例的,以确保消息处理的一致性。
配置文件处理:Spring的Environment抽象和PropertySources是单例的,它们负责加载和解析配置文件,确保配置在整个应用中是一致的。

  • 22
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Leo❀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值