含义
- GoF对单例模式的定义:保证一个类,只有一个实例存在,同时提供能对该实例加以访问的全局访问方法。
注意
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
非线程安全
是否依赖初始化
: 是
是否多线程安全
: 否
总结
:
这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。
e.g.
public class Singleton {
private static Singleton instance;
/// <summary>
/// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
/// </summary>
public static Singleton getInstance() {
// 如果类的实例不存在则创建,否则直接返回
if(instance == null) {
instance = new Singleton();
}
return instance;
}
private Singleton () { }
}
Unity3d 中e.g.
public class Singleton {
private static Singleton instance;
private void Awake ()
{
instance = this;
}
private Singleton () { }
}
简单安全线程
是否依赖初始化
: 是
是否多线程安全
: 是
总结
:
上述实现是线程安全的。这个线程在共享的object上取出了一把锁,然后在创建实例以前检查这个实例是否被创建了。这个保护了内存屏障问题(lock保证了所有的读取操作是在LOCK获得以后发生的,所有的unlock保证了所有的写操作在lock 释放以后发生的),这样就保证了一个线程只能创建一个实例(每次只有一个线程在这段代码中运行),不巧的是,性能上来说,锁变成了每次都必须的当这个实例被响应的时候。
注意除了在锁当中锁住typeof(Singleton)这种类型的以外,我锁住了一个静态私有的变量,对于这个类来说。如果是锁 的一个对象的话,其他的类可以进入并且锁住(比如Type)这样会造成性能风险的问题甚至死锁。
public class Singleton
{
private static Singleton uniqueInstance;
private static readonly object locker = new object();
private Singleton(){}
/// <summary>
/// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
/// </summary>
/// <returns></returns>
public static Singleton GetInstance()
{
// 当第一个线程运行到这里时,此时会对locker对象 "加锁",
// 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
// lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
lock (locker)
{
// 如果类的实例不存在则创建,否则直接返回
if (uniqueInstance == null)
{
uniqueInstance = new Singleton();
}
}
return uniqueInstance;
}
}
线程安全(双重锁定)
是否依赖初始化
: 是
是否多线程安全
: 是
总结
:
- 这个方法java不可行,java的方法可见https://www.cnblogs.com/damonhuang/p/5431866.html
- 需要写得很精确,不然很容易报错,实际开发过程很少用到。
e.g.
public class Singleton
{
private static Singleton uniqueInstance;
private static readonly object locker = new object();
private Singleton(){}
/// <summary>
/// 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
/// </summary>
/// <returns></returns>
public static Singleton GetInstance()
{
// 当第一个线程运行到这里时,此时会对locker对象 "加锁",
// 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
// lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
// 双重锁定只需要一句判断就可以了
if (uniqueInstance == null)
{
lock (locker)
{
// 如果类的实例不存在则创建,否则直接返回
if (uniqueInstance == null)
{
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
线程安全且不需要锁
是否依赖初始化
: 否
是否多线程安全
: 是
总结
:
- 没有加锁,执行效率会提高。
- 类加载时就初始化,浪费内存。
e.g.
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}