单例模式就是保证在整个应用程序的生命周期中,在任何时刻,被指定的类只有一个实例,并为客户程序提供一个获取该实例的全局访问点
一:经典模式:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication3
{
/// <summary>
/// 一、经典模式:
/// </summary>
public class Singleton
{
/// <summary>
/// Singleton的构造函数必须是私有的,以保证客户程序不会通过new()操作产生一个实例,达到实现单例的目的;
/// </summary>
private Singleton(){ }
/// <summary>
/// 私有的静态全局变量instance来保存该类的唯一实例;
/// </summary>
private static Singleton instance;
/// <summary>
/// 提供一个全局函数访问获得该实例,并且在该函数提供控制实例数量的功能,
/// 即通过if语句判断instance是否已被实例化,
/// 如果没有则可以同new()创建一个实例;
/// 否则,直接向客户返回一个实例
/// </summary>
/// <returns></returns>
public static Singleton GetInstance()
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
/// <summary>
/// 测试属性
/// </summary>
public int Age { get; set; }
public void GetShow()
{
this.Age = this.Age + 1;
Console.WriteLine("我是一个单例对象:Age=" + Age);
}
}
}
1)该Singleton的构造函数必须是私有的,以保证客户程序不会通过new()操作产生一个实例,达到实现单例的目的;
2)因为静态变量的生命周期跟整个应用程序的生命周期是一样的,所以可以定义一个私有的静态全局变量instance来保存该类的唯一实例;
3)必须提供一个全局函数访问获得该实例,并且在该函数提供控制实例数量的功能,即通过if语句判断instance是否已被实例化,如果没有则可以同new()创建一个实例;否则,直接向客户返回一个实例。
在这种经典模式下,没有考虑线程并发获取实例问题,即可能出现两个线程同时获取instance实例,且此时其为null时,就会出现两个线程分别创建了instance,违反了单例规则。因此,需对上面代码修改。
二.多线程下的单例模式:1、Lazy模式
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication3
{
/// <summary>
/// 二、多线程下的单例模式
/// 1、Lazy模式
/// </summary>
public class Singleton2
{
private static Singleton2 instance2;
private static object _lock = new object();
private Singleton2()
{
}
public static Singleton2 GetInstance(string thnum)
{
//外层的if语句块,这使得每个线程欲获取实例时不必每次都得加锁,
//因为只有实例为空时(即需要创建一个实例),才需加锁创建,
//若果已存在一个实例,就直接返回该实例,节省了性能开销
if (instance2 == null)
{
//Console.WriteLine("object is null" + thnum);
lock (_lock)
{
//内层的if语句块,使用这个语句块时,先进行加锁操作,
//保证只有一个线程可以访问该语句块而保证只创建了一个实例
if (instance2 == null)
{
//Console.WriteLine("object not null" + thnum);
instance2 = new Singleton2();
instance2.Age = 0;
}
}
}
return instance2;
}
/// <summary>
/// 测试属性
/// </summary>
public int Age { get; set; }
public void GetShow(string thnum)
{
this.Age = this.Age + 1;
Console.WriteLine("我是一个单利对象:Age=" + Age+" 线程:"+thnum);
}
}
}
上述代码使用了双重锁方式较好地解决了多线程下的单例模式实现。
内层的if语句块,使用这个语句块时,先进行加锁操作,保证只有一个线程可以访问该语句块,进而保证只创建了一个实例。
外层的if语句块,这使得每个线程欲获取实例时不必每次都得加锁,因为只有实例为空时(即需要创建一个实例),才需加锁创建,若果已存在一个实例,就直接返回该实例,节省了性能开销。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication3
{
/// <summary>
/// 二、多线程下的单例模式
/// 2、饿汉模式
/// 这种模式的特点是自己主动实例。
/// </summary>
public class Singleton3
{
/// <summary>
/// 主动实例
/// </summary>
private static readonly Singleton3 instance = new Singleton3();
private Singleton3()
{
}
public static Singleton3 GetInstance()
{
return instance;
}
}
}
测试:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("一、经典模式:测试");
//Singleton s = new Singleton(); Error 编译器检测出现错误
Singleton s = Singleton.GetInstance();
s.GetShow();
Singleton s1 = Singleton.GetInstance();
s1.GetShow();
Singleton s12 = Singleton.GetInstance();
s12.GetShow();
Console.WriteLine("二、多线程下的单例模式>Lazy模式:测试");
Thread thr1 = new Thread(x => {
Singleton2 s2 = Singleton2.GetInstance("thr1");
s2.GetShow("thr1");
s2.GetShow("thr1");
});
thr1.Start();
Thread thr2 = new Thread(x => {
Singleton2 s22 = Singleton2.GetInstance("thr2");
s22.GetShow("thr2");
});
thr2.Start();
Console.Read();
}
}
}