我实现的Singleton与Null Object模式是否具备线程安全性?
Singleton与Null Object模式结合实现的线程安全性问题
我尝试编写一段代码结合Singleton模式与Null Object模式,逻辑是判断新客户是否有姓名,有则实例化realcustomer,无则实例化fakecustomer。现在核心疑问是:这段Singleton模式的实现是否具备线程安全性?
interface Icustomer { string Name { get; } bool IsNull { get; } } class realcustomer : Icustomer { public string Name { get; set; } public bool IsNull { get { return false; } } public realcustomer(string name) { Name = name; } } class fakecustomer : Icustomer { public string Name { get { return "customer not available"; } } public bool IsNull { get { return true; } } } class checkifnull { public static Icustomer Getcustomer(string name) { if (string.IsNullOrEmpty(name)) { return new fakecustomer(); } else { return new realcustomer(name); } } } class Singleton { private int total = 0; private static Icustomer cust; private Singleton() { } public static Icustomer makecust(string name) { if (cust == null) { if (string.IsNullOrEmpty(name)) { cust = new fakecustomer(); } else { cust = new realcustomer(name); } } return cust; } public void add() { total++; } public int getTotal() { return total; } } internal class Program { static void Main(string[] args) { Icustomer new_cust = Singleton.makecust("name"); } }
两种模式单独实现时均能正常工作,但我现在尝试将二者结合使用。
核心结论:你的Singleton实现完全不具备线程安全性
问题分析:
- 竞态条件导致多实例创建:多线程环境下,多个线程可同时通过
if (cust == null)的检查,进而重复创建fakecustomer或realcustomer实例,彻底破坏Singleton的单例特性。 - 指令重排序风险:即使某个线程先完成实例化,其他线程可能因CPU指令重排序优化,看到未完全初始化的
cust实例,引发不可预期的运行时错误。 - 类设计逻辑混乱:
Singleton类的makecust是静态方法,但total、add()、getTotal()是实例成员,当前代码从未创建Singleton实例,这些成员完全无法被正确使用,违背了Singleton模式的设计初衷。
线程安全的改进方案
以下是修正后的代码,兼顾线程安全、Singleton与Null Object模式的合理结合:
interface ICustomer { string Name { get; } bool IsNull { get; } } class RealCustomer : ICustomer { public string Name { get; } public bool IsNull => false; public RealCustomer(string name) { Name = name ?? throw new ArgumentNullException(nameof(name)); } } class FakeCustomer : ICustomer { // 无状态的FakeCustomer自身实现单例 private static readonly FakeCustomer _instance = new FakeCustomer(); public static FakeCustomer Instance => _instance; public string Name => "customer not available"; public bool IsNull => true; private FakeCustomer() { } } // 线程安全的Singleton类,管理全局客户实例 class CustomerSingleton { private static ICustomer _customer; private static volatile CustomerSingleton _instance; private static readonly object _lockObj = new object(); private int _total = 0; private CustomerSingleton() { } // 双重检查锁定实现自身的线程安全单例 public static CustomerSingleton Instance { get { if (_instance == null) { lock (_lockObj) { if (_instance == null) { _instance = new CustomerSingleton(); } } } return _instance; } } // 线程安全的客户实例获取逻辑 public ICustomer GetCustomer(string name) { if (_customer == null) { lock (_lockObj) { if (_customer == null) { _customer = string.IsNullOrEmpty(name) ? FakeCustomer.Instance : new RealCustomer(name); } } } return _customer; } // 线程安全的计数操作 public void Add() { lock (_lockObj) { _total++; } } public int GetTotal() { lock (_lockObj) { return _total; } } } internal class Program { static void Main(string[] args) { ICustomer newCust = CustomerSingleton.Instance.GetCustomer("name"); } }
关键改进点:
- 无状态对象单例化:
FakeCustomer无状态,直接做成单例避免重复创建实例。 - 双重检查锁定:配合
volatile关键字,既保证线程安全,又避免每次获取实例都加锁的性能损耗。 - 同步全局状态:对
_total的读写操作加锁,避免多线程下的计数错误。 - 规范命名与合理性:修正C#命名规范,调整
Singleton类的逻辑,确保单例实例被正确使用。
内容的提问来源于stack exchange,提问作者Felix Hajj
相关产品推荐
相关产品推荐

