单例模式:面试七种单例,你能倒背如流么?

102 阅读7分钟

单例模式在订单处理系统中的应用

0. 基础信息

0.0 代码架构图

单例模式代码结构图

0.1 业务架构图

业务流程图-单例

0.2 请求测试标准

本文假设通过 Spring Boot 的 RESTful API 调用各单例服务,测试标准为:

  • 请求路径:/xxxSingleton/process(如 /lazySingleton/process)。
  • 响应格式:ResponseEntity<String>,返回单例模式运行成功的提示信息。
  • 日志输出:通过 @Slf4j 记录每次调用的日志。

1. 代码分析与设计

1.1 单例模式实现

代码提供了七种单例模式实现,分别适用于不同场景:

  1. 懒汉式单例(LazySingletonService

    • 特点:延迟加载,线程不安全。
    • 实现:通过静态变量和同步方法实现。
  2. 饿汉式单例(EagerSingletonService

    • 特点:类加载时即初始化,线程安全。
    • 实现:静态常量直接实例化。
  3. 线程安全的懒汉式单例(ThreadSafeLazySingletonService

    • 特点:延迟加载,使用 synchronized 保证线程安全。
    • 实现:同步方法控制实例创建。
  4. 双重检查锁单例(DoubleCheckedLockingSingletonService

    • 特点:延迟加载,线程安全,性能优化。
    • 实现:使用 volatile 和双重检查减少同步开销。
  5. 静态内部类单例(StaticInnerClassSingletonService

    • 特点:延迟加载,线程安全,利用类加载机制。
    • 实现:静态内部类持有实例。
  6. 枚举单例(EnumSingletonService

    • 特点:线程安全,防止反射和序列化破坏。
    • 实现:枚举类型天然单例。
  7. 注册表单例(SingletonRegistryService

    • 特点:动态管理多个单例实例。
    • 实现:通过 Map 存储实例,支持按类名获取。

1.2 Mermaid 时序图

以下是双重检查锁单例(DoubleCheckedLockingSingletonService)的时序图,展示其调用流程:

sequenceDiagram
    actor User
    participant Controller as DoubleCheckedLockingSingletonController
    participant Service as DoubleCheckedLockingSingletonService

    User->>Controller: GET /doubleCheckedLockingSingleton/process
    Controller->>Service: getInstance()
    alt instance == null
        Service->>Service: synchronized block
        Service->>Service: new DoubleCheckedLockingSingletonService()
    end
    Service-->>Controller: return instance
    Controller->>Service: processWorkflow()
    Service-->>Controller: return "[单例模式-4]..."
    Controller-->>User: ResponseEntity.ok("[单例模式-4]...")

时序图说明

  1. 用户发起 HTTP 请求至 Controller
  2. Controller 调用 getInstance() 获取单例实例。
  3. 若实例未初始化,Service 进入同步块并创建实例。
  4. 调用 processWorkflow() 处理业务并返回结果。

2. 使用单例模式的原因

  1. 资源共享与一致性
    单例模式确保系统中只有一个实例,适用于需要全局唯一的服务(如配置管理、日志服务)。

  2. 控制并发访问
    在多线程环境下,单例模式(尤其是线程安全实现)防止资源竞争和重复初始化。

  3. 延迟加载需求
    懒汉式、双重检查锁和静态内部类实现支持按需加载,避免不必要的资源占用。

  4. 简化系统设计
    单例模式提供全局访问点,减少对象创建和管理成本。


3. 使用单例模式的好处

  1. 内存效率
    只创建一个实例,避免重复分配内存,特别适合资源密集型对象。

  2. 线程安全(部分实现)
    饿汉式、枚举、双重检查锁等实现天然支持多线程环境。

  3. 全局访问便利性
    通过静态方法或枚举直接访问实例,无需传递引用。

  4. 扩展性(注册表单例)
    SingletonRegistryService 支持动态注册多个单例,适应复杂场景。


4. 单例模式的缺陷

  1. 线程安全问题(懒汉式)
    未加同步的懒汉式在多线程下可能创建多个实例。

  2. 性能开销(同步方法)
    ThreadSafeLazySingletonService 的每次调用都需要同步,效率较低。

  3. 资源浪费(饿汉式)
    若实例未被使用,饿汉式仍会提前加载,浪费资源。

  4. 测试困难
    单例的全局状态难以重置,影响单元测试的隔离性。

  5. 反射与序列化风险
    除枚举外,其他实现可能被反射或序列化破坏单例性。


5. 总结

单例模式在订单处理系统中提供了多种实现方式,每种方式针对不同需求权衡了性能、线程安全和资源使用。以下是建议:

  • 简单场景:使用饿汉式或枚举单例,简单且线程安全。
  • 延迟加载:优先选择静态内部类或双重检查锁,兼顾性能和安全。
  • 动态管理:使用注册表单例支持多实例管理。

开发者需根据业务需求选择合适的实现,并注意其缺陷。例如,可通过日志增强调试能力,或结合 Spring 的 @Scope("singleton") 替代手动实现。单例模式虽强大,但应避免滥用,以免增加系统复杂性。

1.代码分析

1.2Service设计

package com.xiaoyongcai.io.designmode.Service.CreationalPatterns.SingletonPattern;
​
import lombok.extern.slf4j.Slf4j;
​
@Slf4j
public class LazySingletonService {
    /**
     * 懒汉式单例模式:
     * 优点:延迟加载,节省资源
     * 缺点:线程不安全,多线程环境下可能会创建多个实例
     * 解决方案:使用synchronized关键字进行同步,保证线程安全
     * 缺点:每次获取实例都需要进行同步,效率较低
     * 解决方案:双重检查锁定(Double-Checked Locking)
     */
    private static LazySingletonService lazySingletonService;
    private LazySingletonService() {};
    public static synchronized LazySingletonService getInstance() {
        if(lazySingletonService == null) {
            lazySingletonService = new LazySingletonService();
        }
        return lazySingletonService;
    }
​
    public String processWorkflow(){
        log.info("[单例模式-1]:线程不安全的懒汉式单例模式运行成功,数据请回看api接口反馈");
        return "[单例模式-1]:线程不安全的懒汉式单例模式运行成功,数据请回看api接口反馈";
    }
}
​
package com.xiaoyongcai.io.designmode.Service.CreationalPatterns.SingletonPattern;
​
import lombok.extern.slf4j.Slf4j;
​
@Slf4j
public class EagerSingletonService {
    private static final EagerSingletonService eagerSingletonService = new EagerSingletonService();
​
    private EagerSingletonService() {
    }
​
    public static EagerSingletonService getInstance() {
        return eagerSingletonService;
    }
​
    public String processWorkflow(){
        log.info("[单例模式-2]:饿汉式单例模式运行成功,数据请回看api接口反馈");
        return "[单例模式-2]:饿汉式单例模式运行成功,数据请回看api接口反馈";
    }
​
}
​
package com.xiaoyongcai.io.designmode.Service.CreationalPatterns.SingletonPattern;
​
import lombok.extern.slf4j.Slf4j;
​
@Slf4j
public class ThreadSafeLazySingletonService {
​
    private static ThreadSafeLazySingletonService instance;
​
    private ThreadSafeLazySingletonService() {}
​
    public static synchronized ThreadSafeLazySingletonService getInstance() {
        if (instance == null) {
            instance = new ThreadSafeLazySingletonService();
        }
        return instance;
    }
​
    public String processWorkflow() {
        // 处理工作流业务
        log.info("[单例模式-3]:线程安全的懒汉单例模式运行成功,数据请回看api接口反馈");
        return "[单例模式-3]:线程安全的懒汉单例模式运行成功,数据请回看api接口反馈";
    }
}
​
package com.xiaoyongcai.io.designmode.Service.CreationalPatterns.SingletonPattern;
​
import lombok.extern.slf4j.Slf4j;
​
/**
 *双重检查锁:为何要在锁中进行第二次检查呢?
 * 因为第一次检查是为了避免不必要的同步,
 * 而第二次检查是确保在进入同步块之前,instance尚未被初始化完成,
 * 避免多线程同时进入同步块导致instance被重复初始化。
 *
 */
@Slf4j
public class DoubleCheckedLockingSingletonService {
    private volatile static DoubleCheckedLockingSingletonService instance;
​
    private DoubleCheckedLockingSingletonService() {}
​
    public static DoubleCheckedLockingSingletonService getInstance() {
        if (instance == null) {
            synchronized (DoubleCheckedLockingSingletonService.class) {
                if (instance == null) {
                    instance = new DoubleCheckedLockingSingletonService();
                }
            }
        }
        return instance;
    }
​
    public String processWorkflow() {
        // 处理工作流业务
        log.info("[单例模式-4]:双重检查锁单例模式运行成功,数据请回看api接口反馈");
        return "[单例模式-4]:双重检查锁单例模式运行成功,数据请回看api接口反馈";
    }
}
​
package com.xiaoyongcai.io.designmode.Service.CreationalPatterns.SingletonPattern;
​
import lombok.extern.slf4j.Slf4j;
​
@Slf4j
public class StaticInnerClassSingletonService {
    /*
    *  静态内部类单例利用了类加载机制保证了线程安全,同时实现了懒加载。
    *  原因是:静态代码块是共有且最先加载,且只加载一次的
    * */
    private StaticInnerClassSingletonService() {}
​
    private static class SingletonHolder {
        private static final StaticInnerClassSingletonService INSTANCE = new StaticInnerClassSingletonService();
    }
​
    public static StaticInnerClassSingletonService getInstance() {
        return SingletonHolder.INSTANCE;
    }
​
    public String processWorkflow() {
        // 处理工作流业务
        log.info("[单例模式-5]:静态内部类单例模式运行成功,数据请回看api接口反馈");
        return "[单例模式-5]:静态内部类单例模式运行成功,数据请回看api接口反馈";
    }
}
​
package com.xiaoyongcai.io.designmode.Service.CreationalPatterns.SingletonPattern;
​
import lombok.extern.slf4j.Slf4j;
​
@Slf4j
public enum EnumSingletonService {
    INSTANCE;
​
    public String processWorkflow() {
        // 处理工作流业务
        log.info("[单例模式-6]:枚举单例模式运行成功,数据请回看api接口反馈");
        return "[单例模式-6]:枚举单例模式运行成功,数据请回看api接口反馈";
    }
}
​
package com.xiaoyongcai.io.designmode.Service.CreationalPatterns.SingletonPattern;
​
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
​
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Service
public class SingletonRegistryService {
​
​
    private static final Map<String, Object> instances = new HashMap<>();
​
    private SingletonRegistryService() {}
​
    public static synchronized Object getInstance(String className) {
​
        if (!instances.containsKey(className)) {
            try {
                instances.put(className,new SingletonRegistryService());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return instances.get(className);
    }
​
    public String processWorkflow() {
        // 处理工作流业务
        log.info( "[单例模式-7]:注册表单例模式运行成功,数据请回看api接口反馈");
        return "[单例模式-7]:注册表单例模式运行成功,数据请回看api接口反馈";
    }
}
​
​

1.3Controller设计

package com.xiaoyongcai.io.designmode.Controller.CreationalPatterns.SingletonPattern;
​
import com.xiaoyongcai.io.designmode.Service.CreationalPatterns.SingletonPattern.LazySingletonService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
​
@RestController
@RequestMapping("/lazySingleton")
public class LazySingletonController {
​
    @GetMapping("/process")
    public ResponseEntity<String> processWorkflow() {
        LazySingletonService lazySingletonService = LazySingletonService.getInstance();
        String result = lazySingletonService.processWorkflow();
        return ResponseEntity.ok(result);
    }
}
​
package com.xiaoyongcai.io.designmode.Controller.CreationalPatterns.SingletonPattern;

import com.xiaoyongcai.io.designmode.Service.CreationalPatterns.SingletonPattern.EagerSingletonService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/eagerSingleton")
public class EagerSingletonController {

    @GetMapping("/process")
    public ResponseEntity<String> processWorkflow() {
        EagerSingletonService eagerSingletonService = EagerSingletonService.getInstance();
        String result = eagerSingletonService.processWorkflow();
        return ResponseEntity.ok(result);
    }
}
package com.xiaoyongcai.io.designmode.Controller.CreationalPatterns.SingletonPattern;

import com.xiaoyongcai.io.designmode.Service.CreationalPatterns.SingletonPattern.ThreadSafeLazySingletonService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/threadSafeLazySingleton")
public class ThreadSafeLazySingletonController {

    @GetMapping("/process")
    public ResponseEntity<String> processWorkflow() {
        ThreadSafeLazySingletonService threadSafeLazySingletonService = ThreadSafeLazySingletonService.getInstance();
        String result = threadSafeLazySingletonService.processWorkflow();
        return ResponseEntity.ok(result);
    }
}
package com.xiaoyongcai.io.designmode.Controller.CreationalPatterns.SingletonPattern;

import com.xiaoyongcai.io.designmode.Service.CreationalPatterns.SingletonPattern.DoubleCheckedLockingSingletonService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/doubleCheckedLockingSingleton")
public class DoubleCheckedLockingSingletonController {

    @GetMapping("/process")
    public ResponseEntity<String> processWorkflow() {
        DoubleCheckedLockingSingletonService doubleCheckedLockingSingletonService = DoubleCheckedLockingSingletonService.getInstance();
        String result = doubleCheckedLockingSingletonService.processWorkflow();
        return ResponseEntity.ok(result);
    }
}
package com.xiaoyongcai.io.designmode.Controller.CreationalPatterns.SingletonPattern;

import com.xiaoyongcai.io.designmode.Service.CreationalPatterns.SingletonPattern.StaticInnerClassSingletonService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/staticInnerClassSingleton")
public class StaticInnerClassSingletonController {

    @GetMapping("/process")
    public ResponseEntity<String> processWorkflow() {
        StaticInnerClassSingletonService staticInnerClassSingletonService = StaticInnerClassSingletonService.getInstance();
        String result = staticInnerClassSingletonService.processWorkflow();
        return ResponseEntity.ok(result);
    }
}
package com.xiaoyongcai.io.designmode.Controller.CreationalPatterns.SingletonPattern;

import com.xiaoyongcai.io.designmode.Service.CreationalPatterns.SingletonPattern.EnumSingletonService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/enumSingleton")
public class EnumSingletonController {

    @GetMapping("/process")
    public ResponseEntity<String> processWorkflow() {
        String result = EnumSingletonService.INSTANCE.processWorkflow();
        return ResponseEntity.ok(result);
    }
}
package com.xiaoyongcai.io.designmode.Controller.CreationalPatterns.SingletonPattern;

import com.xiaoyongcai.io.designmode.Service.CreationalPatterns.SingletonPattern.SingletonRegistryService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/singletonRegistry")
public class SingletonRegistryController {

    @GetMapping("/process/{className}")
    public ResponseEntity<String> processWorkflow(@PathVariable String className) {
        Object instance = SingletonRegistryService.getInstance(className);
        if (instance instanceof SingletonRegistryService) {
            String result = ((SingletonRegistryService) instance).processWorkflow();
            return ResponseEntity.ok(result);
        }
        return ResponseEntity.badRequest().body("Invalid class name");
    }
}