一、实验目的
理解单例模式核心:保证程序全局有且仅有一个类实例对象
掌握线程锁lock作用:解决多线程并发,重复创建单例漏洞
实现跨窗体、跨类调用同一个对象,共享成员变量数据
区分两种单例调用方式:静态方法调用、静态字段直接调用
二、项目文件结构
一共4个代码文件,全部隶属于命名空间:_3单例类
Singleton.cs:单例核心类(加线程锁、私有构造函数)
Form1.cs:WinForm窗体、按钮触发事件
Class1.cs:外部测试类1,调用单例
Class2.cs:外部测试类2,调用单例
三、代码分段精细化整理(保留全部原生注释)
3.1 核心类:Singleton.cs【单例主体】
功能:编写线程安全单例,定义血量、扣血方法,全局唯一对象
namespace _3单例类
{
internal class Singleton
{
//===================== 单例全局字段 =====================
//单例:保证整个项目中有且仅有一个对象
//对外开放一个公共的属性访问点
//获取单例对象,静态字段全局共享
public static Singleton instance = null;
static object locker = new object(); // 线程锁标志
//===================== 私有构造函数 =====================
//防止在外部new 创建对象,把构造函数改成私有的
//封堵外部实例化通道,是实现单例必要条件
private Singleton()
{
}
//===================== 对外获取单例方法 =====================
//对外提供一个方法的访问点
//获取单例对象
public static Singleton GetSingle()
{
// 互斥锁
//线程锁:防止多个线程在同一时间访问这个单例对象
//如果多个线程同时访问,需要排队执行,防止并发创建多个对象
lock (locker)
{
// 如果对象为空了, 再去创建对象
// 如果对象不为空,直接复用已有对象,不再new
if (instance == null)
{
instance = new Singleton();
}
}
return instance;
}
//===================== 业务测试成员 =====================
public int HP = 100;//验证单例对象只有一个:全局共享血量
/// <summary>
/// 扣血方法:每次调用血量减10
/// </summary>
/// <returns>扣减之后的血量</returns>
public int SubHP()
{
return HP -= 10;
}
}
}
3.2 窗体代码:Form1.cs【交互页面】
功能:四个按钮,分别两种方式调用单例、两个外部类调用单例
namespace _3单例类
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//按钮1:实例化Class1,内部调用单例扣血
private void button1_Click(object sender, EventArgs e)
{
Class1 c = new Class1();
label1.Text = c.Attack();
}
//按钮2:实例化Class2,内部调用单例扣血
private void button2_Click(object sender, EventArgs e)
{
Class2 c = new Class2();
label1.Text = c.Attack();
}
//按钮3:通过【静态方法】获取单例对象,执行扣血
private void button3_Click(object sender, EventArgs e)
{
label1.Text = Singleton.GetSingle().SubHP().ToString();
}
//按钮4:直接通过【静态字段】获取单例对象,执行扣血
private void button4_Click(object sender, EventArgs e)
{
label1.Text = Singleton.instance.SubHP().ToString();
}
}
}
3.3 测试类一:Class1.cs
功能:新建独立类,跨类调用全局单例,验证数据共享
namespace _3单例类
{
internal class Class1
{
public string Attack() //定义一个攻击的方法
{
// 调用单例的SubHP方法:属于非静态方法
// 非静态方法不能直接调用,必须获取单例对象调用
// 获取全局唯一单例对象
return Singleton.GetSingle().SubHP().ToString();
}
}
}
3.4 测试类二:Class2.cs
功能:和Class1相互独立,依旧调用同一个单例对象
namespace _3单例类
{
internal class Class2
{
public string Attack() //定义一个攻击的方法
{
// 调用单例的SubHP方法 方法非静态的方法 ,找对象去调用
// 获取单例对象
return Singleton.GetSingle().SubHP().ToString();
}
}
}
四、关键知识点精细注释(考试+作业背诵)
4.1 单例模式三大硬性条件
-
私有构造函数:禁止外部使用new,杜绝随意创建对象
-
静态全局实例:static修饰对象,全局唯一常驻内存
-
对外开放访问入口:静态方法/静态字段,提供唯一访问通道
4.2 lock线程锁作用
不加lock:多线程同时执行,会同时判断instance==null,同时new对象,生成多个实例,单例失效
添加lock:线程排队执行,同一时刻只允许一个线程进入代码块,保证只会创建一次对象
4.3 两种调用单例方式区别
-
方式1:Singleton.GetSingle():调用静态方法获取,安全规范,课堂推荐写法
-
方式2:Singleton.instance:直接读取公共静态字段,写法简便,封装性弱
4.4 实验验证原理
初始HP = 100,无论点击四个任意按钮、无论Class1/Class2哪个类调用,血量持续递减: 证明:全程序共用同一个Singleton对象,单例模式生效
五、运行结果
依次点击按钮,label1显示数值依次:90→80→70→60…… 跨窗体、跨类修改HP值,全局同步变化,不存在独立血量,验证单例成功。





