单例模式:
确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。一个类只有一个实例,即一个类只有一个对象实例。
即要求它的构造方法不能是公开的,既不能被外界进行实例化,即方法为private
怎么实现:
单例模式要求类能够有返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称)。
1:将该类的构造方法定义为私有方法,这样其他处的代码就无法通过调用该类的构造方法来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例;
2:在该类内提供一个静态方法,当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用。
单例模式实现的两个关键点 一:只有一个实例,二:提供一个全局访问点。
只有一个实例:定义静态私有变量(定义为静态主要是为了在多线程确保类有一个实例)
提供一个全局访问点:定义一个公有方法或者属性来把该类的实例公开出去了
public class Singleton
{
/// <summary>
/// 私有变量来记录Singleton的唯一实例
/// </summary>
private static Singleton uniqueInstance;
/// <summary>
/// 私有构造函数
/// </summary>
private Singleton()
{
}
/// <summary>
/// 定义公有方法来提供该类的唯一全局访问点
/// </summary>
/// <returns></returns>
public static Singleton GetInstance()
{
//如实例不存在 则new一个实例 否则返回已有实例
if (uniqueInstance == null)
{
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
上述即为懒汉式单例模式:在类加载时不初始化。
优点:这种写法lazy loading很明显。
缺点:多线程不安全。只适合单线程。
针对缺点:在多线程的情况下会得到多个Singleton实例,因为在两个线程同时运行GetInstance方法时,此时两个线程判断(uniqueInstance ==null)这个条件时都返回真,此时两个线程就都会创建Singleton的实例,这样就违背了我们单例模式初衷了,既然上面的实现会运行多个线程执行,那我们对于多线程的解决方案自然就是使GetInstance方法在同一时间只运行一个线程运行就好了,
注意:lazy loading:延迟加载 或称为懒加载,它的核心思想是把对象的实例化延迟到真正调用该对象的时候,这样做的好处是可以减轻大量对象在实例化时对资源的消耗,而不是在程序初始化的时候就预先将对象实例化。
public class Singleton
{
/// <summary>
/// 私有变量来记录Singleton的唯一实例
/// </summary>
private static Singleton uniqueInstance;
/// <summary>
/// 定义一个标识确保线程同步(解决线程不安全问题)
/// </summary>
private static readonly object locker=new object();
/// <summary>
/// 私有构造函数
/// </summary>
private Singleton()
{
}
/// <summary>
/// 定义公有方法来提供该类的唯一全局访问点
/// </summary>
/// <returns></returns>
public static Singleton GetInstance()
{
// 当第一个线程运行到这里时,此时会对locker对象 加锁,
// 当第二个线程运行到该方法时,首先检测locker对象为加锁状态 该线程就会等待
// lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
lock (locker)
{
// 如果类的实例不存在则创建,否则直接返回
if (uniqueInstance == null)
{
uniqueInstance = new Singleton();
}
}
return uniqueInstance;
}
}
优点:具备很好的lazy loading ,而且加了锁 线程安全
缺点: 我们只需要一次实例化,后面的想获得该类实例,直接return就行了。方法进行同步效率太低要改进。
改进方法:我们只需要在lock语句前面加一句(uniqueInstance==null)的判断就可以避免锁所增加的额外开销,这种实现方式我们就叫它 “双重锁定”。
public class Singleton
{
/// <summary>
/// 私有变量来记录Singleton的唯一实例
/// </summary>
private static Singleton uniqueInstance;
/// <summary>
/// 定义一个标识确保线程同步(解决线程不安全问题)
/// </summary>
private static readonly object locker=new object();
/// <summary>
/// 私有构造函数
/// </summary>
private Singleton()
{
}
/// <summary>
/// 定义公有方法来提供该类的唯一全局访问点
/// </summary>
/// <returns></returns>
public static Singleton GetInstance()
{
//双重锁定
if (uniqueInstance == null)
{
// 当第一个线程运行到这里时,此时会对locker对象 加锁,
// 当第二个线程运行到该方法时,首先检测locker对象为加锁状态 该线程就会等待
// lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
lock (locker)
{
// 如果类的实例不存在则创建,否则直接返回
if (uniqueInstance == null)
{
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
应用与什么:
优点
系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。
缺点
当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new,可能会给其他开发人员造成困扰,特别是看不到源码的时候。
适用场合
- 需要频繁的进行创建和销毁的对象;
- 创建对象时耗时过多或耗费资源过多,但又经常用到的对象;
- 工具类对象;
- 频繁访问数据库或文件的对象。
例子:操作系统中只能有一个任务管理器,操作文件时,同一时间内只允许一个实例对其操作
还可以在控制设备的时候使用 是最方便的