单例模式在订单处理系统中的应用
0. 基础信息
0.0 代码架构图
0.1 业务架构图
0.2 请求测试标准
本文假设通过 Spring Boot 的 RESTful API 调用各单例服务,测试标准为:
- 请求路径:
/xxxSingleton/process(如/lazySingleton/process)。 - 响应格式:
ResponseEntity<String>,返回单例模式运行成功的提示信息。 - 日志输出:通过
@Slf4j记录每次调用的日志。
1. 代码分析与设计
1.1 单例模式实现
代码提供了七种单例模式实现,分别适用于不同场景:
-
懒汉式单例(
LazySingletonService)- 特点:延迟加载,线程不安全。
- 实现:通过静态变量和同步方法实现。
-
饿汉式单例(
EagerSingletonService)- 特点:类加载时即初始化,线程安全。
- 实现:静态常量直接实例化。
-
线程安全的懒汉式单例(
ThreadSafeLazySingletonService)- 特点:延迟加载,使用
synchronized保证线程安全。 - 实现:同步方法控制实例创建。
- 特点:延迟加载,使用
-
双重检查锁单例(
DoubleCheckedLockingSingletonService)- 特点:延迟加载,线程安全,性能优化。
- 实现:使用
volatile和双重检查减少同步开销。
-
静态内部类单例(
StaticInnerClassSingletonService)- 特点:延迟加载,线程安全,利用类加载机制。
- 实现:静态内部类持有实例。
-
枚举单例(
EnumSingletonService)- 特点:线程安全,防止反射和序列化破坏。
- 实现:枚举类型天然单例。
-
注册表单例(
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]...")
时序图说明:
- 用户发起 HTTP 请求至
Controller。 Controller调用getInstance()获取单例实例。- 若实例未初始化,
Service进入同步块并创建实例。 - 调用
processWorkflow()处理业务并返回结果。
2. 使用单例模式的原因
-
资源共享与一致性
单例模式确保系统中只有一个实例,适用于需要全局唯一的服务(如配置管理、日志服务)。 -
控制并发访问
在多线程环境下,单例模式(尤其是线程安全实现)防止资源竞争和重复初始化。 -
延迟加载需求
懒汉式、双重检查锁和静态内部类实现支持按需加载,避免不必要的资源占用。 -
简化系统设计
单例模式提供全局访问点,减少对象创建和管理成本。
3. 使用单例模式的好处
-
内存效率
只创建一个实例,避免重复分配内存,特别适合资源密集型对象。 -
线程安全(部分实现)
饿汉式、枚举、双重检查锁等实现天然支持多线程环境。 -
全局访问便利性
通过静态方法或枚举直接访问实例,无需传递引用。 -
扩展性(注册表单例)
SingletonRegistryService支持动态注册多个单例,适应复杂场景。
4. 单例模式的缺陷
-
线程安全问题(懒汉式)
未加同步的懒汉式在多线程下可能创建多个实例。 -
性能开销(同步方法)
ThreadSafeLazySingletonService的每次调用都需要同步,效率较低。 -
资源浪费(饿汉式)
若实例未被使用,饿汉式仍会提前加载,浪费资源。 -
测试困难
单例的全局状态难以重置,影响单元测试的隔离性。 -
反射与序列化风险
除枚举外,其他实现可能被反射或序列化破坏单例性。
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");
}
}