文章目录
- 1.概念
-
- [1.1 什么是单例模式](#1.1 什么是单例模式)
- [1.2 优点与缺点](#1.2 优点与缺点)
- 2.实现方式
-
- [2.1 懒汉式](#2.1 懒汉式)
-
- [2.1.1 懒汉式(线程不安全)](#2.1.1 懒汉式(线程不安全))
- [2.1.2 懒汉式(线程安全)](#2.1.2 懒汉式(线程安全))
- [2.2 饿汉式](#2.2 饿汉式)
- [2.3 双重检查锁定(Double-Checked Locking)](#2.3 双重检查锁定(Double-Checked Locking))
- [2.4 静态内部类](#2.4 静态内部类)
- [2.5 枚举](#2.5 枚举)
- [3 Java 哪些地方用到了单例模式](#3 Java 哪些地方用到了单例模式)
- [4 Spring 哪些地方用到了单例模式](#4 Spring 哪些地方用到了单例模式)
1.概念
1.1 什么是单例模式
单例模式属于创建型模式
,一个单例类在任何情况下都只存在一个实例
,
构造方法必须是私有的
、由自己创建一个静态变量存储实例,对外提供一
个静态公有方法获取实例。
1.2 优点与缺点
优点 :是内存中只有一个实例,减少了开销,尤其是频繁创建和销毁实例的情况下,可以避免对资源的多重占用。
缺点 :没有抽象层,难以扩展,与单一职责原则
冲突(单例模式由于其全局访问的特性,往往会使得类的使用变得非常广泛,这会导致类的职责膨胀,变得越来越难以维护。)。
2.实现方式
2.1 懒汉式
懒加载 (lazy loading):使用的时候再创建对象。
2.1.1 懒汉式(线程不安全)
在第一次调用获取实例的方法时才创建实例。由于
没有同步措施
,因此线程不安全。
java
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
2.1.2 懒汉式(线程安全)
在懒汉式的基础上,通过
同步代码块或方法
来确保线程安全,但会降低性能。
java
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
2.2 饿汉式
类加载时就创建好实例
,避免了线程同步问题,但可能会导致资源浪费。
java
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
2.3 双重检查锁定(Double-Checked Locking)
结合
懒汉式
和线程安全的特点,在获取实例时进行双重检查,以提高性能。
java
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的类加载机制来实现
懒加载
和线程安全。
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 枚举
使用枚举来实现单例模式,这是最简单的实现方式,并且
天生线程安全
,防止多次实例化。
java
public enum Singleton {
INSTANCE1,
INSTANCE2;
public void someMethod() {
// 功能处理
System.out.println("执行一些功能处理");
}
}
java
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是单例的,它们负责加载和解析配置文件,确保配置在整个应用中是一致的。