在Android开发中,单例模式是常用的设计模式之一,但由于Android的内存管理机制和多线程环境,实现不当容易导致内存泄漏或线程安全问题。以下从线程安全、性能、内存优化等角度分析单例的最佳实现方式:
一、单例模式的核心需求
- 线程安全:确保多线程环境下只创建一个实例。
- 延迟初始化:避免应用启动时即创建实例,降低内存占用。
- 防止反序列化:避免通过反序列化创建新实例。
- 防止反射攻击:禁止通过反射强制创建新实例。
二、常见实现方式及对比
1. 饿汉式(非延迟初始化)
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
}
- 优点:线程安全,实现简单。
- 缺点:不支持延迟初始化,若实例创建代价高(如读取配置、网络请求),可能影响应用启动性能。
2. 懒汉式(延迟初始化,线程不安全)
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 线程不安全:若多个线程同时进入此条件,可能创建多个实例
instance = new Singleton();
}
return instance;
}
}
- 缺点:线程不安全,不推荐使用。
3. 懒汉式(同步方法,线程安全)
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() { // 同步整个方法,性能开销大
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
- 优点:线程安全。
- 缺点:每次调用
getInstance()都需加锁,性能开销大(实际仅首次创建需要同步)。
4. 双重检查锁定(DCL,Double-Checked Locking)
public class Singleton {
private static volatile Singleton instance; // 使用volatile禁止指令重排序
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 第一次检查,无锁
synchronized (Singleton.class) {
if (instance == null) { // 第二次检查,确保只有一个线程创建实例
instance = new Singleton();
}
}
}
return instance;
}
}
- 优点:
- 线程安全。
- 延迟初始化。
- 仅首次创建时同步,后续调用无锁,性能优化。
- 关键点:
volatile关键字:禁止指令重排序,确保实例创建完成后才赋值给instance变量(避免其他线程看到未初始化完成的实例)。- 双重检查:外层检查避免不必要的同步,内层检查确保线程安全。
5. 静态内部类(推荐)
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
- 优点:
- 线程安全:由JVM保证类加载时的线程安全性(静态内部类只加载一次)。
- 延迟初始化:直到调用
getInstance()才加载内部类并创建实例。 - 实现简单,无同步开销。
- 原理:JVM在类加载时会对静态变量和静态代码块进行初始化,且此过程由类加载器保证线程安全。
6. 枚举(最安全,推荐)
public enum Singleton {
INSTANCE;
public void doSomething() {
// 方法实现
}
}
- 优点:
- 线程安全:由JVM保证枚举实例的创建是线程安全的。
- 防止反序列化:枚举类型默认防止通过反序列化创建新实例。
- 防止反射攻击:Java规范禁止通过反射创建枚举实例。
- 缺点:
- 不支持延迟初始化(枚举实例在类加载时即创建)。
- 灵活性较低(如无法继承其他类)。
三、Android中的最佳实践
1. 普通单例(推荐静态内部类)
适用于大多数场景,需延迟初始化且无需处理复杂依赖:
public class AppSettings {
private AppSettings() {
// 初始化操作(如读取配置文件)
}
private static class Holder {
private static final AppSettings INSTANCE = new AppSettings();
}
public static AppSettings getInstance() {
return Holder.INSTANCE;
}
// 业务方法
public void save(String key, String value) {
// ...
}
}
2. 依赖Context的单例(需避免内存泄漏)
- 错误示例:直接持有Activity/Fragment的Context(可能导致内存泄漏):
public class MySingleton { private static MySingleton instance; private Context context; private MySingleton(Context context) { this.context = context; // 若传入Activity/Fragment的Context,可能导致内存泄漏 } public static MySingleton getInstance(Context context) { if (instance == null) { instance = new MySingleton(context); } return instance; } } - 正确示例:使用Application Context(生命周期与应用一致):
public class MySingleton { private static MySingleton instance; private Context context; private MySingleton(Context context) { this.context = context.getApplicationContext(); // 使用Application Context } public static MySingleton getInstance(Context context) { if (instance == null) { instance = new MySingleton(context); } return instance; } }
3. Kotlin实现(推荐对象表达式或伴生对象)
// 方式1:对象表达式(最简洁)
object AppSettings {
init {
// 初始化代码
}
fun save(key: String, value: String) {
// 业务方法
}
}
// 方式2:伴生对象+静态内部类(支持延迟初始化)
class AppSettings private constructor() {
companion object {
val instance: AppSettings by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
AppSettings()
}
}
fun save(key: String, value: String) {
// 业务方法
}
}
- 关键点:
object关键字:Kotlin中声明单例的最简单方式,由编译器保证线程安全。by lazy(LazyThreadSafetyMode.SYNCHRONIZED):延迟初始化,线程安全(默认模式)。
四、总结与选择建议
| 实现方式 | 线程安全 | 延迟初始化 | 防止反序列化 | 防止反射攻击 | 复杂度 | 推荐场景 |
|---|---|---|---|---|---|---|
| 饿汉式 | ✅ | ❌ | ❌ | ❌ | 低 | 简单场景,无需延迟初始化 |
| 懒汉式(同步) | ✅ | ✅ | ❌ | ❌ | 低 | 不推荐(性能差) |
| 双重检查锁定 | ✅ | ✅ | ❌ | ❌ | 中 | Java中复杂场景 |
| 静态内部类 | ✅ | ✅ | ❌ | ❌ | 低 | Java中推荐方案 |
| 枚举 | ✅ | ❌ | ✅ | ✅ | 低 | 需防止反射/反序列化 |
| Kotlin对象 | ✅ | ❌ | ❌ | ❌ | 极低 | Kotlin首选 |
推荐顺序:
- Kotlin项目:优先使用
object关键字,简单高效。 - Java项目:优先使用静态内部类,兼顾线程安全和延迟初始化。
- 需防反射/反序列化:使用枚举(如涉及网络传输或序列化场景)。
无论选择哪种方式,都需注意避免在单例中持有Activity/Fragment等短生命周期对象的引用,防止内存泄漏。
583

被折叠的 条评论
为什么被折叠?



