请问如何保证单例模式只有唯一实例?你知道的都有哪些方法?
Java中的两段检查必须要加上volatile
。因为存在指令重排,给引用赋予内存地址和执行构造函数这两步的顺序是不确定的,因此可能存在如下的情况:
instance
为空,获取锁进行初始化。此时因为重排的原因,先给instance
写入内存地址,然后去执行对象构造,但是构造尚未完成的时候发生了调度 instance
不为空,获取了对象。此时线程1的构造尚未完成 加入volatile
保证happens-before,线程1对instance
的所有写入操作是对线程2可见的,因此不会读到一个半构造的对象。
在C#中,.Net 2.0之后,因为其内存模型规定了同一个线程的一个写操作不能重排至另一个写操作之后,所以.Net 2.0之后可以不需要volatile
。
(需要注意的是C/C++也有volatile
,但是它的语义和Java C#的不一样,不能拿来用作多线程同步。C++需要同步的话需要std::mutex
或std::atomic<T>
)
C++的单例相当简单:
template <class T> class Singleton { public: static T& getInstance() { static T instance; return instance; } };
C++11规定了static变量的初始化即使是多线程环境也只发生一次,而且局部static变量的初始化发生于第一次访问之时,两行代码同时保证惰性初始化和线程安全。不要class这层裹脚布能更精简一点:
template <class T> T& getInstance() { static T instance; return instance; }