单例模式是一种创建型设计模式,确保一个类只有一个实例并提供全局访问点。它用于解决资源管理、全局访问、状态保持和代码简化等问题。在Java中,通过私有化构造函数和提供静态方法实现。使用单例模式可节省系统资源、提高代码可维护性和一致性,避免不一致状态,并简化代码结构和逻辑。
定义
在软件开发中,我们经常遇到一些资源,它们的数量有限或初始化成本很高,比如数据库连接、线程池和缓存。每次需要这些资源时都重新创建它们,不仅效率低下,还会浪费宝贵的系统资源。这时,单例模式就派上了用场。
单例模式是一种设计模式,它确保一个类只有一个实例,并提供一个全局访问点,这意味着,无论你的应用程序的哪个部分需要这个实例,都可以通过同一个地方获取到。这样一来,那些高成本或稀缺的资源就能够在整个应用程序中被共享和重用,从而大大节省了系统资源。
除了资源控制外,单例模式还提供了一个全局的访问点,这极大地提高了代码的可维护性和一致性,因为开发者不需要记住或查找不同的实例创建方式,只需要通过单例模式提供的方法就能轻松获取到所需的实例。
此外,有些对象需要保持状态,比如配置管理器或日志记录器,通过单例模式,我们可以确保这些对象的状态在整个应用程序中保持一致,避免了因多个实例各自维护状态而导致的混乱和不可预测性。此外,单例模式还能简化代码的结构和逻辑。
由于单例模式的实例是全局可访问的,开发者无需将实例作为参数在方法或类之间传递,这不仅减少了代码的复杂性,还使得代码更加清晰易读。
代码案例
在软件开发中,单例模式主要用于以下场景:
- 资源控制:对于数据库连接、线程池、缓存等系统资源,通过单例模式确保全局只有一个实例,从而有效控制资源的创建和销毁。
- 全局状态管理:当系统中某个对象的状态需要全局共享时,单例模式可以确保该对象的状态一致性。
- 配置管理:应用程序的配置信息通常只需要加载一次,并在整个应用生命周期中保持不变,单例模式可以确保配置信息的唯一性和全局可访问性。
单例模式有两种比较常见的写法:懒汉模式和饿汉模式。懒汉模式与饿汉模式是实现单例模式的两种主要策略。懒汉模式延迟实例化对象,仅在首次使用时创建,节省资源但多线程下需考虑同步问题。相反,饿汉模式在类加载时即完成实例化,确保线程安全但可能导致不必要的资源占用,如下代码所示:
懒汉模式
懒汉模式的特点是延迟加载,即在第一次使用时才创建实例。
反例,非线程安全的实现,在多线程环境下可能导致创建多个实例,代码如下:
/**
* @版权 Copyright by 程序员古德 <br>
* @创建人 程序员古德 <br>
* @创建时间 2023/12/19 16:37 <br>
*/
public class Singleton {
private static Singleton instance; // 没有使用volatile修饰,可能存在可见性问题
private Singleton() {} // 私有构造方法
public static Singleton getInstance() {
if (instance == null) { // 没有进行双重检查锁定,可能导致多个实例被创建
instance = new Singleton(); // 创建实例
}
return instance; // 返回实例
}
}
正例,使用双重检查锁定确保线程安全,代码如下:
/**
* @版权 Copyright by 程序员古德 <br>
* @创建人 程序员古德 <br>
* @创建时间 2023/12/19 16:37 <br>
*/
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; // 返回实例
}
}
饿汉模式
饿汉模式的特点是提前加载,即在类加载时就创建实例。
反例,尝试使用非final
静态变量,这可能导致在某些情况下重新赋值,代码如下:
/**
* @版权 Copyright by 程序员古德 <br>
* @创建人 程序员古德 <br>
* @创建时间 2023/12/19 16:37 <br>
*/
public class Singleton {
private static Singleton instance = new Singleton(); // 注意这里没有使用final修饰符
private Singleton() {}
public static Singleton getInstance() {
return instance; // 如果其他代码修改了instance,就可能违反单例模式的约束。
}
}
正例,线程安全的实现,在类加载时直接初始化静态实例,无需考虑多线程同步问题,代码如下:
/**
* @版权 Copyright by 程序员古德 <br>
* @创建人 程序员古德 <br>
* @创建时间 2023/12/19 16:37 <br>
*/
public class Singleton {
private static final Singleton instance = new Singleton(); // 在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快。
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
核心总结
单例模式,作为创建型设计模式之一,在软件开发中占据重要地位。它确保某个类只有一个实例,并提供一个全局访问点。
这种模式的适用性广泛,尤其在需要频繁创建和销毁对象的场景中,单例模式能有效减少系统开销。通过隐藏类的构造函数,单例模式避免了外部代码随意创建对象,从而确保对象的唯一性。在实现上,主要有懒汉式和饿汉式两种方式,各有优缺点。懒汉式延迟实例化对象,只在首次使用时创建,但多线程环境下需要额外同步机制保证线程安全;饿汉式则在类加载时就完成实例化,天然线程安全但可能导致资源浪费。正确选择和使用单例模式,能够提高系统的性能和稳定性。同时,理解其背后的设计思想和适用场景,对于提升软件设计能力和解决实际问题具有重要意义。