单例模式:全局唯一实例的优雅实现
摘要:单例模式(Singleton)确保一个类只有一个实例,并提供全局访问点。本文详解其原理、Android应用场景、UML、实战对比及优缺点,助你深入掌握这一核心设计模式。
1.概念
单例模式的核心思想是:
- 私有化构造函数:禁止外部直接创建实例。
- 静态私有实例:类内部持有唯一实例的引用。
- 全局访问点:通过静态方法(如
getInstance()
)获取实例。
关键代码示例: public class Singleton { private static Singleton instance; // 静态私有实例
private Singleton() {} // 私有构造
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton(); // 延迟初始化
}
return instance;
}
}
2.在Android源码中的应用场景
2.1 LayoutInflater
LayoutInflater inflater = LayoutInflater.from(context);
内部通过单例缓存避免重复创建解析器。
2.2 Retrofit
public class RetrofitClient {
private static class Holder {
private static final Retrofit INSTANCE = new Retrofit.Builder()
.baseUrl(BASE_URL)
.build();
}
public static Retrofit getInstance() {
return Holder.INSTANCE;
}
}
3.UML图
┌─────────────┐
│ Singleton │
├─────────────┤
│ - instance │
├─────────────┤
│ - Singleton()│
│ + getInstance()│
└─────────────┘
▲
│
唯一持有自身实例
单列实现原理图:
Singleton
:包含静态私有实例instance
。- 私有构造:防止外部实例化。
getInstance()
:控制实例创建并提供全局访问。
4.项目的例子和没用设计模式的对比
以下是一个在语音识别项目中应用单例模式的真实示例
语音引擎管理器: 它封装了语音识别、合成和状态管理的核心功能:
4.1 使用单例模式
public class SpeechConstant {
public static final int TYPE_CLOUD = 1;
public static final int ENGINE_TYPE = 2;
public static final String VOICE_NAME = "";
}
public class SpeechSynthesizer {
public static SpeechSynthesizer createSynthesizer(Object o) {
return null;
}
public void destroy() {
}
public boolean isSpeaking() {
return true;
}
public void stopSpeaking() {
}
public void startSpeaking(String text, Object o) {
}
public void setParameter(String voiceName, String xiaoyan) {
}
}
public class VoiceEngineManager {
// 单例实例(volatile保证多线程可见性)
private static volatile VoiceEngineManager instance;
// 语音识别引擎
private SpeechRecognizer recognizer;
// 语音合成引擎
private SpeechSynthesizer synthesizer;
// 当前语音识别状态
private boolean isRecognizing = false;
// 私有构造器
private VoiceEngineManager() {
initSpeechEngine();
}
/**
* 获取单例实例(双重检查锁实现线程安全)
*/
public static VoiceEngineManager getInstance() {
if (instance == null) {
synchronized (VoiceEngineManager.class) {
if (instance == null) {
instance = new VoiceEngineManager();
}
}
}
return instance;
}
/**
* 初始化语音引擎
*/
private void initSpeechEngine() {
// 初始化语音识别引擎
recognizer = SpeechRecognizer.createRecognizer( null);
recognizer.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);
// 初始化语音合成引擎
synthesizer = SpeechSynthesizer.createSynthesizer(null);
synthesizer.setParameter(SpeechConstant.VOICE_NAME, "xiaoyan");
}
/**
* 开始语音识别
*/
public void startRecognition(RecognitionListener listener) {
if (!isRecognizing) {
recognizer.startListening(listener);
isRecognizing = true;
Log.d("VoiceEngine", "语音识别已启动");
}
}
/**
* 停止语音识别
*/
public void stopRecognition() {
if (isRecognizing) {
recognizer.stopListening();
isRecognizing = false;
Log.d("VoiceEngine", "语音识别已停止");
}
}
/**
* 语音合成播报
*/
public void speak(String text) {
if (synthesizer.isSpeaking()) {
synthesizer.stopSpeaking();
}
synthesizer.startSpeaking(text, null);
}
/**
* 释放引擎资源
*/
public void release() {
if (recognizer != null) {
recognizer.destroy();
}
if (synthesizer != null) {
synthesizer.destroy();
}
isRecognizing = false;
}
// 语音识别监听器接口
public interface RecognitionListener {
void onResults(String result);
void onError(int errorCode);
}
static class Log{
public static void d(String voiceEngine, String tag) {
}
}
}
5.优点
- 资源优化:避免重复创建(如数据库连接池)。
- 状态一致性:全局唯一实例保证数据准确。
- 访问便捷:通过静态方法随处获取实例。
- 扩展性:可派生子类实现灵活的单例逻辑(如按线程区分)。
6.和相识的设计模式的区别
基本没有
模式 | 核心目标 | 与单例的区别 |
---|---|---|
静态工具类 | 提供无需实例的公共方法 | 无状态存储,单例可持有成员变量 |
享元模式 | 复用细粒度对象 | 管理多例,单例仅维护唯一实例 |
依赖注入 | 解耦对象创建与使用 | 单例主动控制实例,DI由容器管理实例 |
6.2 单例模式的几种类型
6.2.1 双重检查锁 (DCL)
public class DCLSingleton {
// volatile保证可见性和禁止指令重排序
private static volatile DCLSingleton instance;
private DCLSingleton() {
// 防止反射攻击
if (instance != null) {
throw new IllegalStateException("Already initialized");
}
}
public static DCLSingleton getInstance() {
// 第一次检查(无锁)
if (instance == null) {
synchronized (DCLSingleton.class) {
// 第二次检查(加锁后)
if (instance == null) {
instance = new DCLSingleton();
}
}
}
return instance;
}
}
6.2.1.1 为什么要2次判空?
核心目的:性能优化与线程安全的完美平衡
双重判空的必要性:
-
第一层判空(无锁检查) :
- 解决99%的读操作性能问题
- 当实例已存在时,直接返回(避免锁竞争)
- 减少同步块执行次数(性能提升5-100倍)
-
第二层判空(同步块内检查) :
- 防止重复创建实例
- 处理首次初始化时的竞态条件
- 确保多线程环境下仅创建唯一实例
6.2.1.2 为什么要用synchronized?
核心作用:解决写操作的原子性
创建过程非原子: 多个线程
6.2.1.3 为什么要用volatile?
禁止指令重排序
// 无volatile时可能发生的错误场景:
Thread A:
instance = new Singleton(); // 步骤2和3重排
→ 1.分配内存
→ 3.赋值引用 (此时instance!=null)
→ 2.初始化对象 (未完成)
Thread B:
if (instance != null)
// 使用未初始化的对象 → NPE!
6.2.2 静态内部类 (DCL)
public class HolderSingleton {
private HolderSingleton() {
// 防止反射攻击
if (Holder.INSTANCE != null) {
throw new IllegalStateException("Already initialized");
}
}
public static HolderSingleton getInstance() {
return Holder.INSTANCE;
}
// 静态内部类在首次调用getInstance()时加载
private static class Holder {
private static final HolderSingleton INSTANCE = new HolderSingleton();
}
}
6.2.3 枚举单例 (DCL)
public enum EnumSingleton {
INSTANCE;
// 业务方法
public void doSomething() {
System.out.println("Enum Singleton Working");
}
}
单例的3种模式的对比:
版本 | 线程安全 | 防反射 | 防序列化 | 延迟加载 | 适用场景 |
---|---|---|---|---|---|
双重检查锁 (DCL) | ✅ | ❌ | ❌ | ✅ | 高并发服务端 |
静态内部类 | ✅ | ✅ | ❌ | ✅ | Android/桌面应用 |
枚举单例 | ✅ | ✅ | ✅ | ❌ | 分布式系统/安全敏感场景 |
总结:单例模式是控制实例数量的利器,尤其在需要全局状态管理(如配置、缓存)的场景中不可替代。但需警惕滥用——过度使用会导致代码耦合,建议结合依赖注入框架(如Dagger)优化架构。
项目的地址: github.com/pengcaihua1…