背景
在Unity开发中,单例是非常实用并且是最简单的设计模式。
顾名思义,单例模式就是:
保证类仅有一个实例对象,并且提供唯一的全局访问点。
简单来讲,单例模式必须具备以下条件:
- 单例类只有一个实例对象。
- 单例类必须给其他所有对象提供这一实例。
- 单例类构造函数只能在内部调用。
再引用一个比较有意思的例子,这个例子非常形象的介绍了单例模式:
俺有6个漂亮的老婆,她们的老公都是我,我就是我们家里的老公 Singleton,她们只要说到“老公”,都是指的同一个人,那就是我(刚才做了个梦啦,哪有这么好的事)。-《泡妞与设计模式》
单例模式实现
单例模式分为懒汉式和饿汉式。
- 懒汉式:当程序第一次访问单例模式实例时才进行创建。
- 饿汉式:在单例模式类被加载的时候,单例模式实例就已经被创建。
懒汉式单例实现
懒汉式是典型的时间换空间,也就是每次获取实例都会进行判断,看是否需要创建实例,浪费判断的时间。当然,如果一直没有人使用的话,那就不会创建实例,则节约内存空间。
1 | public class Singleton |
饿汉式单例实现
饿汉式是典型的空间换时间,当类装载的时候就会创建类实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要再判断了,节省了运行时间。
1 | public class Singleton |
线程安全的单例模式
线程安全的懒汉式单例
从线程安全性上讲,不加同步的懒汉式是线程不安全的,两个线程可能同时判断if (mInstance == null)结果为true,于是都创建了实例,这样就违背了单例模式。那么如何实现线程安全的懒汉式的单例呢?
1 | public class Singleton |
这个实现是线程安全的。
- 当第一个线程运行到这里时,此时会对mLocker对象 “加锁”。
- 当第二个线程运行该方法时,首先检测到mLocker对象为”加锁”状态,该线程就会挂起等待第一个线程解锁。
- mLocker语句运行完之后(即线程运行完之后)会对该对象”解锁”。
双重检查加锁
由于锁机制的代价比较高,每次都要判断锁机制,比较消耗性能,那么有没有更好的方式实现呢?
可以使用”双重检查加锁”的方式来实现,就可以既实现线程安全,又能够使性能不受到很大的影响。那么什么是”双重检查加锁”机制呢?
所谓双重检查加锁机制指的是:并不是每次获取Instance都需要同步,进入方法体后,先检查实例是否存在,如果不存在才进入下面的同步块,这是第一重检查。进入同步块过后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查。这样一来,就只需要同步一次了,从而减少了多次在同步情况下进行判断所浪费的时间。
1 | public class Singleton |
泛型单例模式
如果有多个单例类的话,每个类中都有这段代码,那该怎么办呢?
答案是引入泛型。
1 | public class Singleton<T> where T : Singleton<T>, new() |
可以看到泛型T的基类类型需是Singleton<T>,并且带有一个public的构造函数来实例化对象,但是如果这样,这个单例类岂不是可以在任意的地方实例化对象了吗?既然这样,就不能通过约束T:new()来实例化,那该怎么办呢?我们还可以通过反射机制来实例化一个对象,具体代码如下:
1 |
|