【Spring】单例模式的创建方式(Bean解析)

在Java中,单例模式(Singleton Pattern)确保一个类只有一个实例,并提供全局访问点。以下是实现单例的五种常见方式:懒汉式饿汉式双重检查锁静态内部类枚举,包括代码示例和优缺点分析。


1. 懒汉式(Lazy Initialization)

  • 特点:延迟加载,实例在第一次使用时创建。

  • 线程安全:基本实现非线程安全,需加锁优化。

  • 代码示例

    java 复制代码
    public class Singleton {
        private static Singleton instance;
    
        private Singleton() {} // 私有构造,防止外部实例化
    
        public static Singleton getInstance() {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
    • 线程安全版 (加锁):

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

    • 延迟加载,节省内存(只有在使用时才创建实例)。
  • 缺点

    • 基本版线程不安全。
    • 线程安全版使用synchronized方法锁,性能较低(每次调用都加锁)。
  • 适用场景:单线程环境或实例创建开销小、对性能要求不高的场景。


2. 饿汉式(Eager Initialization)

  • 特点:类加载时就创建实例,急切初始化。

  • 线程安全:天然线程安全,依赖JVM类加载机制。

  • 代码示例

    java 复制代码
    public class Singleton {
        private static final Singleton instance = new Singleton();
    
        private Singleton() {}
    
        public static Singleton getInstance() {
            return instance;
        }
    }
  • 优点

    • 实现简单,线程安全(由JVM类加载保证)。
    • 无需加锁,性能较高。
  • 缺点

    • 非延迟加载,类加载时即创建实例,可能浪费内存(如果实例未被使用)。
    • 如果构造方法有复杂逻辑,类加载可能变慢。
  • 适用场景:实例创建开销小、确定会被使用的场景。


3. 双重检查锁(Double-Checked Locking, DCL)

  • 特点:结合懒汉式的延迟加载和线程安全,减少锁粒度。

  • 线程安全 :通过volatile和双重检查确保线程安全。

  • 代码示例

    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防止指令重排序,确保实例初始化完成前不被其他线程访问。
    • 双重检查减少锁竞争,仅在实例未创建时加锁。
  • 优点

    • 延迟加载,节省内存。
    • 线程安全,性能较高(锁粒度小)。
  • 缺点

    • 实现较复杂,需理解volatile和指令重排序。
    • 早期Java版本(1.4及之前)可能有DCL失效问题(现已解决)。
  • 适用场景:需要延迟加载且高并发的场景。


4. 静态内部类(Static Inner Class)

  • 特点:利用JVM类加载机制实现延迟加载和线程安全。

  • 线程安全:由JVM保证静态内部类加载时的线程安全。

  • 代码示例

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

    • 静态内部类SingletonHoldergetInstance()调用时才加载,触发INSTANCE初始化。
    • JVM确保类加载过程线程安全,无需额外同步。
  • 优点

    • 延迟加载,节省内存。
    • 线程安全,无需加锁,性能高。
    • 实现优雅,代码简洁。
  • 缺点

    • 无法传递参数给构造方法(除非通过其他方式)。
  • 适用场景:需要延迟加载且要求高性能的场景,推荐使用。


5. 枚举(Enum)

  • 特点:使用Java枚举类型实现单例,简洁且天然线程安全。

  • 线程安全:由JVM保证枚举实例的单例性。

  • 代码示例

    java 复制代码
    public enum Singleton {
        INSTANCE;
    
        // 可添加方法
        public void doSomething() {
            System.out.println("Singleton method called");
        }
    }
    • 使用方式:

      java 复制代码
      Singleton.INSTANCE.doSomething();
  • 优点

    • 实现最简单,代码极少。
    • 线程安全,由JVM保证。
    • 防止反射攻击和序列化问题(枚举天生防止反序列化创建新实例)。
    • 支持添加方法,功能灵活。
  • 缺点

    • 非延迟加载,枚举类加载时即创建实例。
    • 不支持复杂构造逻辑(枚举构造较为固定)。
  • 适用场景:需要绝对线程安全、防止反射/序列化问题、逻辑简单的场景。


6. 对比总结

方式 延迟加载 线程安全 性能 复杂性 防止反射/序列化 适用场景
懒汉式 否/是(加锁) 低(锁) 需额外处理 单线程或低并发
饿汉式 需额外处理 确定使用、无内存限制
双重检查锁 需额外处理 高并发、延迟加载
静态内部类 需额外处理 高性能、延迟加载,推荐
枚举 天然支持 简单逻辑、防反射/序列化,推荐

7. 注意事项

  • 私有构造:所有实现都需私有构造方法,防止外部实例化。

  • 反射攻击 :除枚举外,其他方式可能通过反射创建实例,需在构造方法中加防护:

    java 复制代码
    private Singleton() {
        if (instance != null) {
            throw new RuntimeException("Instance already exists!");
        }
    }
  • 序列化问题 :除枚举外,单例实现序列化时需实现readResolve方法:

    java 复制代码
    private Object readResolve() {
        return instance;
    }
  • Spring中的单例:Spring的单例是容器级别的,生命周期由Spring管理,通常无需手动实现单例模式。


8. 推荐方式

  • 首选静态内部类(延迟加载、线程安全、实现优雅)。
  • 次选枚举(最简单、防反射/序列化,适合简单场景)。
  • 高并发双重检查锁 (需确保正确使用volatile)。
  • 确定使用饿汉式(简单直接)。
  • 避免懒汉式(除非单线程或加锁优化)。
相关推荐
一直走下去-明几秒前
spring-cloud-stream学习
spring·中间件
熊大如如6 小时前
Java 反射
java·开发语言
猿来入此小猿7 小时前
基于SSM实现的健身房系统功能实现十六
java·毕业设计·ssm·毕业源码·免费学习·猿来入此·健身平台
goTsHgo7 小时前
Spring Boot 自动装配原理详解
java·spring boot
卑微的Coder7 小时前
JMeter同步定时器 模拟多用户并发访问场景
java·jmeter·压力测试
pjx9878 小时前
微服务的“导航系统”:使用Spring Cloud Eureka实现服务注册与发现
java·spring cloud·微服务·eureka
多多*8 小时前
算法竞赛相关 Java 二分模版
java·开发语言·数据结构·数据库·sql·算法·oracle
爱喝酸奶的桃酥8 小时前
MYSQL数据库集群高可用和数据监控平台
java·数据库·mysql
唐僧洗头爱飘柔95279 小时前
【SSM-SSM整合】将Spring、SpringMVC、Mybatis三者进行整合;本文阐述了几个核心原理知识点,附带对应的源码以及描述解析
java·spring·mybatis·springmvc·动态代理·ioc容器·视图控制器
骑牛小道士9 小时前
Java基础 集合框架 Collection接口和抽象类AbstractCollection
java