设计模式-单利模式
一、单例模式
确保一个类仅有一个实例,并提供一个全局访问点,避免一个全局使用的类频繁的创建和销毁。
二、何时使用
一个公司只有一个老板
一个学校只有一个校长
操作系统只有一个任务管理器
三、如何实现
下面使用C#代码展示
public class Singleton
{
// 单例实例,声明为 instance,避免外部调用
private static Singleton instance = null;
// 将构造函数声名为 private 避免外部调用
private Singleton()
{
}
// 提供一个外部访问函数
public Singleton GetInstance()
{
if (null == instance)
{
instance = new Singleton();
}
return instance;
}
}
上面代码在单线程中是没问题的,但是在多线程中会存在线程不安全。
如果两个线程同时访问 GetInstance() 方法且 instance 为 空, 则 if (null == instance) 这个条件在两个线程判断都为 真(true),此时两个线程都会创建 Singleton 实例,破坏了我们的规则。
改进如下
public class Singleton
{
// 单例实例,声明为 instance,避免外部调用
private static Singleton instance = null;
// 定义一个标示,确认线程安全
private static readonly object locker = new object();
// 将构造函数声名为 private 避免外部调用
private Singleton()
{
}
// 提供一个外部访问函数
public Singleton GetInstance()
{
// 当第一个线程运行到这里时,此时会对 locker "加锁"
// 当第二个线程运行到这里时,检测到 locker 对象为 "加锁" 状态,
// 第二个线程就会挂起等待第一个线程执行结束
// lock 语句运行完之后,会对该对象 "解锁"
lock (locker)
{
if (null == instance)
{
instance = new Singleton();
}
}
return instance;
}
}
改进后的代码是线程安全了,在多线程调用过程中也不会出现多次创建的问题了,但是却产生了新的问题,当多个线程访问 GetInstance() 函数,运行到 lock (locker) 的时候,每个线程都会在这里 “加锁”,其他线程需要在此挂起等待,如果此时 instance 已经创建过了,不为空,其他线程在此挂起等待,浪费了多线程的效率,还需要再次修改
改进如下
public class Singleton
{
// 单例实例,声明为 instance,避免外部调用
private static Singleton instance = null;
// 定义一个标示,确认线程安全
private static readonly object locker = new object();
// 将构造函数声名为 private 避免外部调用
private Singleton()
{
}
// 提供一个外部访问函数
public Singleton GetInstance()
{
// 每个线程运行到这里先判断是否为空,如果不为空
// 则直接跳过此模块
if (null == instance)
{
// 当第一个线程运行到这里时,此时会对 locker "加锁"
// 当第二个线程运行到这里时,检测到 locker 对象为 "加锁" 状态,
// 第二个线程就会挂起等待第一个线程执行结束
// lock 语句运行完之后,会对该对象 "解锁"
lock (locker)
{
if (null == instance)
{
instance = new Singleton();
}
}
}
return instance;
}
}
优点:
内存中只有一个实例,减少了内存开销(一个对象频繁的创建、销毁 降低性能)
全局都可以轻松访问
缺点:
没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化