首页 > 试题广场 >

请问如何保证单例模式只有唯一实例?你知道的都有哪些方法?

[问答题]

请问如何保证单例模式只有唯一实例?你知道的都有哪些方法?

Java中的两段检查必须要加上volatile。因为存在指令重排,给引用赋予内存地址和执行构造函数这两步的顺序是不确定的,因此可能存在如下的情况:

  1. 线程1发现instance为空,获取锁进行初始化。此时因为重排的原因,先给instance写入内存地址,然后去执行对象构造,但是构造尚未完成的时候发生了调度
  2. 线程2发现instance不为空,获取了对象。此时线程1的构造尚未完成

加入volatile保证happens-before,线程1对instance的所有写入操作是对线程2可见的,因此不会读到一个半构造的对象。

在C#中,.Net 2.0之后,因为其内存模型规定了同一个线程的一个写操作不能重排至另一个写操作之后,所以.Net 2.0之后可以不需要volatile

(需要注意的是C/C++也有volatile,但是它的语义和Java C#的不一样,不能拿来用作多线程同步。C++需要同步的话需要std::mutexstd::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;
}
编辑于 2019-09-03 15:24:59 回复(0)
饿汉式在一开始就初始化实例,是线程安全的;懒汉式在要用的时候才创建实例,所以要先判断实例是否已经存在。
发表于 2020-06-26 21:08:53 回复(0)