C#设计模式---- 01. 单例模式
一. 特征
- 单例模式最明显特征, 在整个进程只有一个实例(注意进程和线程的区分)
- 针对集群系统, 在集群的每个容器, 只有一个实例, 但是, 不同的容器, 是不同的实例(未做集群可以忽略)
二. 角色类
- 实例类(Instance):
- 客户端(Client)
三. 实现
主要分为2大类: 饿汉式, 懒汉式, 每个类别中又细分为几个小类, 如下:
1. 饿汉式
a. 静态字段(属性)
//定义类
public class Instance
{
private static Instance _instance = new Instance(); //声明私有化静态字段(也可以声明属性), 并赋值
private Instance() //一般私有化构造函数, 避免客户端直接new实例
{
Console.WriteLine("构造函数Instance 执行......");
}
public static Instance GetInstance() // 声明静态方法, 获取实例
{
return _instance; // 返回声明的字段(属性)
}
public void Test()//声明个普通方法, 测试实例是否存在
{
Console.WriteLine("执行测试方法 .....");
}
}
b. 静态方法
和静态属性区别点: 声明字段(属性)不赋初始值, 在静态构造函数里面赋值
//定义类
public class Instance
{
private static Instance _instance;//声明私有化静态字段(也可以声明属性), <<不赋值>>
private Instance() //一般私有化构造函数, 避免客户端直接new实例
{
Console.WriteLine("构造函数Instance 执行......");
}
static Instance()
{
_instance = new Instance();//对字段赋初始值
}
public static Instance GetInstance() // 声明静态方法, 获取实例
{
return _instance; // 返回声明的字段(属性)
}
public void Test()//声明个普通方法, 测试实例是否存在
{
Console.WriteLine("执行测试方法 .....");
}
}
2. 懒汉式
a. 单if判null
不能用: 可能出现构造多次, 不能保证单例
//定义类
public class Instance
{
private static Instance _instance;//声明私有化静态字段(也可以声明属性), 不赋值
private Instance() //一般私有化构造函数, 避免客户端直接new实例
{
Console.WriteLine("构造函数Instance 执行......");
}
//--------------重点---------------
public static Instance GetInstance() // 声明静态方法, 获取实例
{
if(_instance == null)//单if判null
{
_instance = new Instance();
}
return _instance; // 返回声明的字段(属性)
}
//--------------重点---------------
public void Test()//声明个普通方法, 测试实例是否存在
{
Console.WriteLine("执行测试方法 .....");
}
}
b. if判null,外层加lock
lock作用, 保证进入lock块的代码块是单线程的
不推荐: 多线程通过lock变成单线程, 无法利用多线程的优势, 意义不大
//定义类
public class Instance
{
private static Instance _instance;//声明私有化静态字段(也可以声明属性), 不赋值
private Instance() //一般私有化构造函数, 避免客户端直接new实例
{
Console.WriteLine("构造函数Instance 执行......");
}
//--------------重点---------------
private static readonly object _instanceLock = new object(); //声明静态lock对象
public static Instance GetInstance() // 声明静态方法, 获取实例
{
lock (_instanceLock)
{
if (_instance == null)//单if判null
{
_instance = new Instance();
}
}
return _instance; // 返回声明的字段(属性)
}
//--------------重点---------------
public void Test()//声明个普通方法, 测试实例是否存在
{
Console.WriteLine("执行测试方法 .....");
}
}
c. 单lock外层加if判null
lock作用, 保证进入lock块的代码块是单线程的
不能用: 和单if判null一样, 不能保证单例
//定义类
public class Instance
{
private static Instance _instance;//声明私有化静态字段(也可以声明属性), 不赋值
private Instance() //一般私有化构造函数, 避免客户端直接new实例
{
Console.WriteLine("构造函数Instance 执行......");
}
//--------------重点---------------
private static readonly object _instanceLock = new object();
public static Instance GetInstance() // 声明静态方法, 获取实例
{
if (_instance == null)
{
lock (_instanceLock)
{
_instance = new Instance();
}
}
return _instance; // 返回声明的字段(属性)
}
//--------------重点---------------
public void Test()//声明个普通方法, 测试实例是否存在
{
Console.WriteLine("执行测试方法 .....");
}
}
d. lock 内外均加if判null
lock作用, 保证进入lock块的代码块是单线程的
推荐: 保证单例, 而且能充分利用多线程
//定义类
public class Instance
{
private static Instance _instance;//声明私有化静态字段(也可以声明属性), 不赋值
private Instance() //一般私有化构造函数, 避免客户端直接new实例
{
Console.WriteLine("构造函数Instance 执行......");
}
//--------------重点---------------
private static readonly object _instanceLock = new object();
public static Instance GetInstance() // 声明静态方法, 获取实例
{
if (_instance == null)
{
lock (_instanceLock)
{
if (_instance == null)//单if判null
{
_instance = new Instance();
}
}
}
return _instance; // 返回声明的字段(属性)
}
//--------------重点---------------
public void Test()//声明个普通方法, 测试实例是否存在
{
Console.WriteLine("执行测试方法 .....");
}
}
e. 成员类(类中类)
推荐: 借助类的实例化机制, 在未使用的时候, 不会占用内存, 实现懒汉式, 是前面2个饿汉式实例的一个进化版本, 一样可以使用静态字段(属性),或者静态构造方法
//定义类
public class Instance
{
private Instance() //一般私有化构造函数, 避免客户端直接new实例
{
Console.WriteLine("构造函数Instance 执行......");
}
//--------------重点---------------
private class SubClass // 定义成员类
{
public static Instance _instance = new Instance();//声明私有化静态字段(也可以声明属性), 不赋值
}
public static Instance GetInstance() // 声明静态方法, 获取实例
{
return SubClass._instance;// 返回成员类字段(属性)
}
//--------------重点---------------
public void Test()//声明个普通方法, 测试实例是否存在
{
Console.WriteLine("执行测试方法 .....");
}
}
3. 客户端调用
客户端一个是单线程的, 一个是多线程的方法, 可以都运行以下, 加深理解
public class Client
{
public void Run()
{
Console.WriteLine("单线程客户端启动......");
Console.WriteLine("---------");
for (int i = 0; i < 20; i++)
{
Instance instance = Instance.GetInstance();
instance.Test();
}
Console.WriteLine("---------");
}
public void TaskRun()
{
Console.WriteLine("多线程客户端客户端启动......");
Console.WriteLine("---------");
for (int i = 0; i < 20; i++)
{
Task.Run(()=>{
Instance instance = Instance.GetInstance();
instance.Test();
});
}
Console.WriteLine("---------");
}
}