简介
单例对象的类只能允许一个实例存在。
许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为,比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。
Singleton类,定义一个GetInstance操作,允许客户访问它的唯一实例。GetInstance是一个静态方法,主要负责创建唯一实例。
实现步骤
- 将该类的构造方法定义为私有方法,这样其他处的代码就无法通过调用该类的构造方法来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例;
- 在该类内提供一个静态方法,当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用。
适用场景:
- 1.需要生成唯一序列的环境
- 2.需要频繁实例化然后销毁的对象。
- 3.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
- 4.方便资源相互通信的环境
优缺点
优点:
- 在内存中只有一个对象,节省内存空间;
- 避免频繁的创建销毁对象,可以提高性能;
- 避免对共享资源的多重占用,简化访问;
- 为整个系统提供一个全局访问点。
缺点:
- 不适用于变化频繁的对象;
- 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;
- 如果实例化的对象长时间不被利用,系统会认为该对象是垃圾而被回收,这可能会导致对象状态的丢失;
实现方式
1.饿汉式
// 饿汉式单例
public class Singleton1 {
// 指向自己实例的私有静态引用,主动创建
private static Singleton1 singleton1 = new Singleton1();
// 私有的构造方法
private Singleton1(){}
// 以自己实例为返回值的静态的公有方法,静态工厂方法
public static Singleton1 getSingleton1(){
return singleton1;
}
}
类加载的方式是按需加载,且加载一次,类加载的时候便创建了一个实例。
优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。
缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。
2.懒汉式
// 懒汉式单例
public class Singleton2 {
// 指向自己实例的私有静态引用
private static Singleton2 singleton2;
// 私有的构造方法
private Singleton2(){}
// 以自己实例为返回值的静态的公有方法,静态工厂方法
public static Singleton2 getSingleton2(){
// 被动创建,在真正需要使用时才去创建
if (singleton2 == null) {
singleton2 = new Singleton2();
}
return singleton2;
}
}
单例实例被延迟加载,即只有在真正使用的时候才会实例化一个对象并交给自己的引用
这种写法起到了Lazy Loading的效果,但是只能在单线程下使用。如果在多线程下,一个线程进入了
**if (singleton == null)**判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式。
3.双重加锁机制
public class SingletonDemo3 {
private volatile static SingletonDemo3 singletonDemo3;
private SingletonDemo3(){}
public static SingletonDemo3 getSingletonDemo3(){
if(singletonDemo3==null){
synchronized (SingletonDemo3.class){
if(singletonDemo3==null){
singletonDemo3 = new SingletonDemo3();
}
}
}
return singletonDemo3;
}
}
使用双重检测同步延迟加载去创建单例的做法是一个非常优秀的做法,其不但保证了单例,而且切实提高了程序运行效率
优点:线程安全;延迟加载;效率较高。
4.静态初始化
//阻止发生派生,而派生可能会增加实例
public sealed class Singleton
{
//在第一次引用类的任何成员时创建实例,公共语言运行库负责处理变量初始化
private static readonly Singleton instance=new Singleton();
private Singleton() { }
public static Singleton GetInstance()
{
return instance;
}
}
在实际应用中,C#与公共语言运行库也提供了一种“静态初始化”方法,这种方法不需要开发人员显式地编写线程安全代码,即可解决多线程环境下它是不安全的问题。