一、什么是单例模式
单例模式就是保证进程中,某一个类型只有一个实例。
如图:
此时Student被构造了5次,单例模式就是要实现Student只被构造一次。
二、单例模式的作用
单例就是对象的重用,例如连接数据库的时候,就不用多次连接,只需要连接一次,防止程序乱连接导致数据库连接池爆满,而且当连接和释放数据库连接时非常耗时,大概就这么个意思。
三、单例模式的使用场景
当使用的类要求只能在程序中出现一次时就需要使用单例模式。
例如:数据库连接池,线程池,流水号生成等等。。
四、如何实现单例模式
首先需要将类的构造函数私有化(使用private关键字),防止其他人随意调用类的构造函数,然后创建一个新的方法,并将此方法暴露出去供其他人使用。
第一种写法(双if加锁):
public class Student
{
private Student()
{
Console.WriteLine("Student被构造了");
}
private static Student _Student = null;
public static Student NewClass()
{
if (_Student == null)
{
_Student = new Student();
}
return _Student;
}
}
此时在主线程中实现了单例,但是还是会有问题,就是多个线程同时调用时还是会被多次实例化,请看下图:
在多线程运行时,就会被多次实例化,这是不符合需求的,需要进一步修改。
public class Student
{
private Student()
{
Console.WriteLine("Student被构造了");
}
private static Student _Student = null;
private static readonly object lockStudent = new object();
public static Student NewClass()
{
if (_Student == null)
{
lock (lockStudent)
{
if (_Student == null)
{
_Student = new Student();
}
}
}
return _Student;
}
}
这是经典的双if判断,请看下图执行结果:
这里使用了双if加锁的方式实现了类的单例,当第一个线程进来的时候,是可以通过这三层并且实例化类的,其他线程就会在外面等待,当实例化完成之后在第一个if就会跳出,不会进入到锁等待的阶段,这样就减少了性能消耗。
到此就实现了单例,这是第一种写法。
第二种写法(静态构造函数):
public class Student
{
private Student()
{
Console.WriteLine("Student被构造了");
}
static Student()
{
_Student = new Student();
}
private static Student _Student = null;
public static Student NewClass()
{
return _Student;
}
}
静态构造函数在程序中任何时候第一次使用此类之前自动调用一次,不会重复调用。
以下为执行结果:
到此就实现了单例,这是第二种写法。
第三种写法(静态字段):
public class Student
{
private Student()
{
Console.WriteLine("Student被构造了");
}
private static Student _Student = new Student();
public static Student NewClass()
{
return _Student;
}
}
这个与上一个方法差不多,以下为执行结果:
五、单例模式的优缺点
单例模式并不能防止线程冲突。
实现了单例模式的类可以在全局中保持唯一,可以随时使用,看下图:
如果此类不是单例,哪么这个地方应该是1,因为类被重新实例化哪么num就会被重新赋值,当类是单例模式时,就不会出现这种情况,哪么不管在哪里调用这个num都会输出被处理后的值。
大概就这么个意思。
完。