单例模式介绍
单例模式只允许被其自身实例化一次,且向外部提供了一个访问该实例的接口。
通常来说,单例对象进行实例化时一般不带参数,因为如果不同的实例化请求传递的参数不同的话会导致问题的产生。
单例模式主要特点
-
全局唯一性:单例模式确保一个类只有一个实例,无论多少次尝试创建该类的实例,都只会返回第一次创建的实例。这提供了一种全局访问点,使得在程序的任何地方都可以访问和使用这个唯一的实例。
-
线程安全性:在多线程环境中,单例模式需要确保实例的创建是线程安全的,以防止多个线程同时创建多个实例。这通常通过同步机制(如双重检查锁定)来实现。
-
延迟初始化(懒加载)或预初始化(饿加载):单例模式可以通过不同的实现方式来实现实例的创建时机。懒加载(懒汉式)意味着实例在第一次被请求时创建,而预初始化(饿汉式)则意味着实例在类加载时就已经创建。懒加载可能带来线程安全的问题,而预初始化则不存在这个问题,但可能会浪费一些资源,因为实例可能在程序执行过程中从未被使用。
-
封装性:单例模式将类的实例化过程封装在类内部,隐藏了实例化的细节,对外只提供一个全局的访问点。这有助于隐藏实现细节,并提供了一个统一的接口来获取类的实例。
-
限制实例化:单例模式通过私有构造函数和静态方法限制了类的实例化过程,确保只有单例类自己能够创建实例,外部代码无法直接通过
new
关键字来创建新的实例。 -
简化配置:在某些场景中,单例模式可以简化对象的配置和管理。例如,当某个类需要访问数据库连接池、日志记录器或其他共享资源时,可以通过单例模式来管理这些资源的唯一实例,避免资源的重复创建和浪费。
需要注意的是,虽然单例模式在某些情况下很有用,但它也可能导致代码难以理解和维护。过度使用单例模式可能导致代码结构混乱、依赖关系复杂以及测试困难。因此,在使用单例模式时应该谨慎考虑,并确保它真正符合你的设计目标和需求。
单例模式常见使用方式
饿汉式(静态初始化)
在这种模式下,Singleton类在加载时就会立即创建并初始化一个静态实例,所以它是线程安全的,但可能造成资源浪费,如果程序从未使用到该实例。
public sealed class Singleton
{
private static readonly Singleton instance = new Singleton();
// 将构造函数设为私有,防止外部直接实例化
private Singleton() {}
// 提供一个公共的静态属性来获取实例
public static Singleton Instance
{
get { return instance; }
}
}
懒汉式(线程不安全)
这种实现不是线程安全的,当多线程同时访问Instance
属性并发现instance
为空时,可能会创建多个实例。
public sealed class Singleton
{
private static Singleton instance;
private Singleton() {}
public static Singleton Instance
{
get
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
}
懒汉式(双重检查锁定 - 线程安全)
双重检查锁定解决了线程安全问题,在多线程环境下确保了只有一个实例被创建。volatile
关键字确保了多线程环境下的可见性和有序性。
public sealed class Singleton
{
private static volatile Singleton instance;
private static object syncRoot = new Object();
private Singleton() {}
public static Singleton Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
{
instance = new Singleton();
}
}
}
return instance;
}
}
}
静态内部类(线程安全且延迟加载)
使用静态内部类的方式实现单例模式既保证了线程安全,又实现了延迟加载,即当首次访问Singleton.Instance时才会真正实例化对象。
public sealed class Singleton
{
private Singleton() {}
public static Singleton Instance
{
get
{
return Nested.instance;
}
}
private class Nested
{
// 静态初始化只会在第一次调用Nested.instance时发生
internal static readonly Singleton instance = new Singleton();
}
}
枚举模式(最简洁、线程安全)
如果单例可以接受无参数构造函数,并且不需要额外的方法和状态,可以通过枚举实现单例模式,这种方式是线程安全的并且自动处理序列化问题。不过它适用于非常简单的场景,因为枚举不能扩展其他功能或行为。
public enum Singleton
{
Instance
}
单例模式总结
在单例类的内部实现只生成一个实例,同时它提供一个静态的getInstance()工厂方法,让客户可以访问它的唯一实例;为了防止在外部对其实例化,将其构造函数设计为私有;在单例类内部定义了一个Singleton类型的静态对象,作为外部共享的唯一实例。
1.资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如日志文件,应用配置。
2.控制资源的情况下,方便资源之间的互相通信。如线程池等。
3.一般情况下,我们并不需要实现完全懒汉式,除非构造初始化执行了某些费时的工作。因此一般的,我们使用显式的静态构造函数就能够适用。
参考地址:
https://www.cnblogs.com/mingnianjiehunba/p/17669212.html
https://www.jb51.net/article/261666.htm
其他等