class STWindow
{
static Window win;
private STWindow() { }
public static Window GetInstance()
{
if (win == null || !win.IsVisible)
{
win = new Window();
win.Height = 200;
win.Width = 200;
}
return win;
}
}
class Singleton
{
static Singleton _singleton;
private Singleton() { }
public static Singleton GetInstance()
{
if (_singleton == null)
{
_singleton = new Singleton();
}
return _singleton;
}
}
1. 多线程同时创建实例问题
多个线程同时访问 Singleton 的 GetInstance() 方法,可能会造成创建多个实例;
比如 A 先进入后在准备实例化 Singleton 的过程中,B 也进入了,这时 _singleton 还是为 null,然后 B 也去实例化 Singleton 了。
class Singleton
{
static Singleton _singleton;
private Singleton() { }
public static Singleton GetInstance()
{
if (_singleton == null)
{
Console.WriteLine("实例化一个 Singleton");
_singleton = new Singleton();
}
Console.WriteLine("返回一个 Singleton");
return _singleton;
}
}
System.Threading.Thread a = new System.Threading.Thread(() =>
{
Singleton.GetInstance();
});
System.Threading.Thread b = new System.Threading.Thread(() =>
{
Singleton.GetInstance();
});
System.Threading.Thread c = new System.Threading.Thread(() =>
{
Singleton.GetInstance();
});
a.Start();
b.Start();
c.Start();
实例化一个 Singleton
实例化一个 Singleton
返回一个 Singleton
返回一个 Singleton
返回一个 Singleton
我们通过加个锁来解决这个问题。
public static readonly object syncRoot = new object();
public static Singleton GetInstance()
{
lock (syncRoot)
{
if (_singleton == null)
{
Console.WriteLine("实例化一个 Singleton");
_singleton = new Singleton();
}
}
Console.WriteLine("返回一个 Singleton");
return _singleton;
}
实例化一个 Singleton
返回一个 Singleton
返回一个 Singleton
线程 0x1a08 已退出,返回值为 0 (0x0)。
返回一个 Singleton
线程 0x45b4 已退出,返回值为 0 (0x0)。
线程 0x5a04 已退出,返回值为 0 (0x0)。
2. 每次实例都要锁的性能问题
使用双重锁定,只有未实例化时才锁,锁完判断是否还未实例化。
public static Singleton GetInstance()
{
if (_singleton == null)
{
lock (syncRoot)
{
if (_singleton == null)
{
_singleton = new Singleton();
}
}
}
return _singleton;
}
3.饿汉式单例
class Singleton
{
static readonly Singleton _instance = new Singleton();
private Singleton() { }
public static Singleton GetInstance()
{
return _instance;
}
}
这种静态初始化的方式是自己被加载时就将自己实例化,因此被称为饿汉式单例;
而第一次被引用时才将自己实例化的方式被称为懒汉式单例。C# 中饿汉式已足够满足。