定义
- 保证一个类仅有一个实例(私有的静态变量,当前创建了下次不用再次创建),并提供一个它的全局访问点
- 要保证是唯一的实例,没有其他实例创建
使用场景
- 创建实例太耗费资源,只允许使用一个公共实例,比如 I/O 与数据库的连接等
- 全局计数器
代码实现
//懒汉单例类:在第一次被引用时,才将自己实例化
class Singleton
{
private static Singleton instance; //静态实例,第一次赋值后,将不再为空
private Singleton(){ } //禁止默认初始化
public static Singleton GetInstance()
{
if(instance == null)
{
instance = new Singleton();
}
return instance;
}
}
void Main()
{
//Singleton s1 = new Singleton() //无法实例化,访问级别限制
Singleton s1 = Singleton.GetInstance();
Singleton s2 = Singleton.GetInstance();
if(s1 == s2)
{
Console.WriteLine("两个对象是相同的实例");
}
}
//多线程的实例<涉及到可能会创建多个实例 使用 lock锁>
/*
* lock 可确保当一个线程位于代码的临界区时,另一个线程不会进入该临界区。
* 如果其他线程尝试进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放
*
* lock 锁的是引用类型的对象,string 类型除外
* 引用类型推荐 静态的、只读的、私有的 对象
* 保证lock的对象在外部无法修改才有意义,如果lock的对象在外部改变了,对其他线程就会畅通无阻,失去了lock的意义。
*/
class Singleton
{
private static Singleton instance;
private static readonly object syncRoot = new object();
private Singleton(){ }
//单锁
public static Singleton GetInstance()
{
lock(syncRoot)
{
if (instance == null)
{
instance = new SafeSingleton();
}
}
return instance;
}
//DCL double checked locking 双检查锁机制 双重锁定 最佳实践
public static Singleton GetInstance()
{
if(instance == null) // 如果存在,则return ,否则进入,免去等待时间
{
lock(syncRoot) //必须有,如果没有 则可能造成多实例情况
{
if(instance == null) //必须有,如果在lock锁排队有2个进入,不加判断,第2个也能创建实例
{
instance = new Singleton();
}
}
}
return instance;
}
}
//静态初始化实例
//饿汉单例类:在自己在被加载时就将自己实例化,会提前占用系统资源,但是解决了多线程不安全问题
public sealed class Singleton //sealed 避免派生,派生可以增加实例
{
private static readonly Singleton instance = new Singleton();
private Singleton() { }
public static Singleton GetInstance()
{
return instance;
}
}
优点
- 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存
- 避免对资源的多重占用(比如写文件操作)
- 单例模式具有一定的伸缩性,类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性
- 提供了对唯一实例的受控访问
- 允许可变数目的实例
- 避免对共享资源的多重占用
缺点
- 没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化
- 不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态
- 由于单例模式中没有抽象层,因此单例类的扩展有很大的困难
- 单例类的职责过重,在一定程度上违背了“单一职责原则”
- 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失