【设计模式】单例模式

1,689 阅读4分钟

单例模式

背景

单例模式(Singleton Pattern)是一种创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点。单例模式广泛应用于Android开发中,例如管理全局状态、缓存、日志记录、数据库连接等。

单例模式的优点

  1. 节约资源:单例模式确保一个类只有一个实例,减少内存开销,节约系统资源。
  2. 全局访问:提供全局访问点,方便在不同的组件中共享实例。
  3. 控制实例数量:避免了多个实例带来的数据不一致性问题。

实现单例模式

在Java和Kotlin中,都有多种实现单例模式的方法。以下将分别介绍Java和Kotlin中的实现方式。

Java中的单例模式实现

  1. 懒汉式:在需要时才创建实例,线程不安全。

public class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

  1. 线程安全的懒汉式:使用同步方法保证线程安全。

public class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

  1. 双重检查锁定:减少同步开销,保证线程安全。

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;
    }
}

优化后的双重检查锁定单例模式

双重检查锁定单例模式的核心思想是:在第一次检查时,如果实例已经存在,则不需要进入同步块;只有在实例为空时,才会进入同步块进行实例化,这样可以减少同步的开销。

  1. 确保使用 volatile 关键字

在Java中,为了确保变量的可见性和有序性,instance 需要使用 volatile 关键字声明。volatile 确保了多个线程处理同一个实例变量时的正确性。


public class Singleton {
    // 使用 volatile 关键字,确保 instance 的可见性和有序性
    private static volatile Singleton instance;

    // 私有构造函数,防止外部创建实例
    private Singleton() {
        // 防止反射创建实例
        if (instance != null) {
            throw new IllegalStateException("Already initialized.");
        }
    }

    // 双重检查锁定的单例获取方法
    public static Singleton getInstance() {
        if (instance == null) { // 第一次检查
            synchronized (Singleton.class) { // 同步块
                if (instance == null) { // 第二次检查
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

  1. 防止反射创建实例

在单例类的构造函数中添加一个判断,如果 instance 不为空,则抛出异常,以防止通过反射创建实例。


private Singleton() {
    if (instance != null) {
        throw new IllegalStateException("Already initialized.");
    }
}

实现 Serializable 接口并重写 readResolve 方法,以防止通过序列化和反序列化创建新实例。


import java.io.Serializable;

public class Singleton implements Serializable {
    private static volatile Singleton instance;

    private Singleton() {
        if (instance != null) {
            throw new IllegalStateException("Already initialized.");
        }
    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }

    // 防止序列化破坏单例
    protected Object readResolve() {
        return getInstance();
    }
}

静态内部类:利用类加载机制保证线程安全。


public class Singleton {
    private Singleton() {}

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

枚举单例:实现简单,线程安全,防止反序列化创建新实例。


public enum Singleton {
    INSTANCE;

    public void doSomething() {
        // 实例方法
    }
}

Kotlin中的单例模式实现

  1. 对象声明:Kotlin提供了简洁的单例实现方式。

object Singleton {
    fun doSomething() {
        // 实例方法
    }
}

  1. 伴生对象:在类内部定义单例。

class Singleton private constructor() {
    companion object {
        private var instance: Singleton? = null

        fun getInstance(): Singleton {
            if (instance == null) {
                instance = Singleton()
            }
            return instance!!
        }
    }
}

单例模式在Android中的应用

  1. 全局状态管理:例如管理用户会话、配置等。

public class UserManager {
    private static UserManager instance;
    private User user;

    private UserManager() {}

    public static UserManager getInstance() {
        if (instance == null) {
            instance = new UserManager();
        }
        return instance;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}

  1. 网络请求管理:例如管理OkHttp的单例实例。

public class OkHttpClientManager {
    private static OkHttpClientManager instance;
    private OkHttpClient client;

    private OkHttpClientManager() {
        client = new OkHttpClient();
    }

    public static OkHttpClientManager getInstance() {
        if (instance == null) {
            instance = new OkHttpClientManager();
        }
        return instance;
    }

    public OkHttpClient getClient() {
        return client;
    }
}

  1. 数据库管理:例如Room数据库的单例实例。

@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
    private static volatile AppDatabase INSTANCE;

    public abstract UserDao userDao();

    public static AppDatabase getInstance(Context context) {
        if (INSTANCE == null) {
            synchronized (AppDatabase.class) {
                if (INSTANCE == null) {
                    INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
                            AppDatabase.class, "app_database")
                            .build();
                }
            }
        }
        return INSTANCE;
    }
}

注意事项

  1. 线程安全:在多线程环境下,确保单例模式的实现是线程安全的。
  2. 资源释放:在应用退出或不再需要单例实例时,及时释放资源,避免内存泄漏。
  3. 避免反序列化破坏单例:使用枚举单例可以防止反序列化创建新实例的问题。

单例模式在Android开发中有广泛的应用,通过合理使用单例模式,可以有效管理全局状态、减少资源消耗、提高应用的稳定性。然而,在使用单例模式时需要注意线程安全和资源释放问题,以避免潜在的内存泄漏和数据不一致性。希望本文的介绍能够帮助你更好地理解和应用单例模式。