单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
一个普通的单例
public class Singleton
{
//定义一个私有的静态全局变量来保存该类的唯一实例
private static Singleton instance;
// 构造函数必须是私有的这样在外部便无法使用 new 来创建该类的实例
private Singleton() { }
//定义一个全局访问点,设置为静态方法,则可以实现在类的外部无需实例化就可以调用该方法
public static Singleton Instance
{
get
{
//这里可以保证只实例化一次,即在第一次调用时实例化,之后调用不会实例化
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
}
这个普通的单例模式,其实还是会有几个要点,我们先来看看
- Singleton 模式中的实例和构造器可以设置为protected以允许子类派生。
- Singleton 模式一般不要支持ICloneable接口,因为这可能会导致多个对象实例,与Singleton模式的初衷违背。
- Singleton 模式一般不要支持序列化,因为这也有可能导致多个对象实例,同样与Singleton模式的初衷违背。
- Singleton 模式只考虑到了对象创建的管理,没有考虑对象销毁的管理。就支持垃圾回收的平台和对象的开销来讲,我们一般没有必要对其销毁进行特殊的管理。
-
不能应对多线程环境:在多线程环境中,使用Singleton模式仍然有可能得到Singleton类的多个实例对象。
如果在一开始调用 GetInstance()时,是由两个线程同时调用的(这种情况是很常见的),注意是同时,
(或者是一个线程进入 if 判断语句后但还没有实例化 Singleton 时,第二个线程到达,此时 singleton 还是为 null)这样的话,两个线程均会进入 GetInstance(),而后由于是第一次调用 GetInstance(),所以存储在 Singleton 中的静态变量 singleton 为 null ,这样的话,就会让两个线程均通过 if 语句的条件判断,然后调用 new Singleton()了,
一个简单的单例
public class Singleton
{
public static readonly Singleton Instance = new Singleton();
private Singleton() { }
}
简单的原因是使用了readonly 修饰符。单例模式的初始化由前面的static静态函数实现,然后在运行时编译,
当这个类被加载的时候,会自动实例化这个类,就不用在后面调用get才实例化出唯一的对象了,而且无需我们关心线程安全性[问题]底层会自动解决。
但是小有小的好处,也不可避免带来一点麻烦,比如这样写的话就不支持构造器接受参数了,所以我们得小拆分下,让程序变得更灵活。
public class Singleton
{
public static readonly Singleton Instance;
static Singleton()
{
Instance = new Singleton();
}
private Singleton() { }
}
现在我们把new的那块代码拆分,留到构造器实例化重新生成,这样就可以使用重载来另外加带传参的方法了。
从这里再来总结单例模式的特点:
首先,单例模式使类在程序生命周期的任何时刻都只有一个实例,
然后,单例的构造函数是私有的,外部程序如果想要访问这个单例类的话,
必须通过 GetInstance()来请求(注意是请求)得到这个单例类的实例。
全局变量和单例模式的区别
首先,全局变量呢就是对一个对象的静态引用,全局变量确实可以提供单例模式实现的全局访问这个功能,
但是,它并不能保证您的应用程序中只有一个实例,同时,在编码规范中,也明确指出,
应该要少用全局变量,因为过多的使用全局变量,会造成代码难读,
还有就是全局变量并不能实现继承(虽然单例模式在继承上也不能很好的处理,但是还是可以实现继承的)
而单例模式的话,其在类中保存了它的唯一实例,这个类,它可以保证只能创建一个实例,
同时,它还提供了一个访问该唯一实例的全局访问点。