精华内容
下载资源
问答
  • 线程安全单例模式
    2022-01-28 06:16:17

    Python微信订餐小程序课程视频

    https://edu.csdn.net/course/detail/36074

    Python实战量化交易理财系统

    https://edu.csdn.net/course/detail/35475

    饿汉式

    饿汉式:类加载就会导致该单实例对象被创建

    复制代码
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    java`// 问题1:为什么加 final
    // 问题2:如果实现了序列化接口, 还要做什么来防止反序列化破坏单例
    public final class Singleton_hungry implements Serializable {

    // 问题3:为什么设置为私有? 是否能防止反射创建新的实例?
    private Singleton_hungry(){}

    // 问题4:这样初始化是否能保证单例对象创建时的线程安全?
    private static Singleton_hungry INSTANCE = new Singleton_hungry();

    // 问题5:为什么提供静态方法而不是直接将 INSTANCE 设置为 public, 说出你知道的理由
    public static Singleton_hungry getInstance() {
    return INSTANCE;
    }
    public Object readResolve(){ // 防止反射创建新的实例?
    return INSTANCE;
    }
    }`

    • 问题1:
      避免子类覆盖父类的一些方法,导致线程不安全。
    • 问题2:
      实现 readResolve 方法。当从对象流 ObjectInputStream 中读取对象时,会检查对象的类否定义了 readResolve 方法。如果定义了,则调用它返回我们想指定的对象(这里就指定了返回单例对象)。
    • 问题3:防止通过 new 创建对象实例。不能防止反射创建新的实例。
    • 问题4:可以。静态变量初始化在类加载时进行,由 jvm 进行管理,可以保证线程安全。
    • 问题5:通过方法,可以提高拓展性,改进饿汉式转化为懒汉式、利用泛型特性、增加对单例对象的控制操作。

    枚举单例

    复制代码
    
    • 1
    • 2
    • 3

    javaenum Singleton { INSTANCE; }

    • 问题1:枚举单例是如何限制实例个数的
      单例相当于枚举的静态成员变量,定义几个就有几个实例。
    • 问题2:枚举单例在创建时是否有并发问题
      单例相当于枚举的静态成员变量,类加载时初始化,由 jvm 进行管理,可以保证线程安全。
    • 问题3:枚举单例能否被反射破坏单例
      不能
    • 问题4:枚举单例能否被反序列化破坏单例
      枚举实现了 Serializable 接口,可序列化,但不会被反序列破坏单例。
    • 问题5:枚举单例属于懒汉式还是饿汉式
      饿汉式
    • 问题6:枚举单例如果希望加入一些单例创建时的初始化逻辑该如何做
      枚举允许构造方法

    懒汉式

    复制代码
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    typescriptpublic final class Singleton\_lazy { private Singleton\_lazy(){} private static Singleton_lazy INSTANCE = null; // 缺点 public static synchronized Singleton_lazy getInstance() { if(INSTANCE != null) { return INSTANCE; } INSTANCE = new Singleton_lazy(); return INSTANCE; } }

    • synchronized 保证线程安全,但锁粒度较大,性能低。

    DCL 懒汉式

    复制代码
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    java`public final class Singleton_DCL {

    private Singleton_DCL() {}

    // 问题1:解释为什么要加 volatile ?
    private static volatile Singleton_DCL INSTANCE= null;

    // 问题2:对比实现3, 说出这样做的意义
    public static Singleton_DCL getInstance() {
    if(INSTANCE != null) {
    return INSTANCE;
    }
    synchronized (Singleton_DCL.class) {

    // 问题3:为什么还要在这里加为空判断, 之前不是判断过了吗
    if(INSTANCE != null) {
    return INSTANCE;
    }
    INSTANCE = new Singleton_DCL();
    return INSTANCE;
    }
    }
    }`

    • 问题1:避免指令重排序,导致赋值语句先于构造函数执行,得到一个未初始化完毕的对象。
    • 问题2、3:Double Check Lock 机制。同步代码块外部的判断语句主要用于 INSTANCE 初始化并赋值之后,此时 INSTANCE != null,如果有多个线程尝试获取单例,可以提前返回,不用执行同步代码块。而同步代码块内部的判断主要用于第一次初始化时,INSTANCE = null,此时可以有多个线程尝试获取 INSTANCE,只能有一个线程进入同步代码块,其他线程在同步代码块外阻塞,该线程创建一个单例对象之后,唤醒其他线程,再进入同步代码块,发现 INSTANCE != null,则直接返回,不用重新创建单例对象,提高了效率。

    静态内部类懒汉单例

    复制代码
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    java`public final class Singleton_LazyHolder {
    private Singleton_LazyHolder(){}

    // 问题1:属于懒汉式还是饿汉式
    private static class LazyHolder{
    static final Singleton_LazyHolder INSTANCE = new Singleton_LazyHolder();
    }

    // 问题2:在创建时是否有并发问题
    public static Singleton_LazyHolder getInstance() {
    return LazyHolder.INSTANCE;
    }
    }`

    • 问题1:懒汉式。静态内部类只有在被方法调用的时候才进行初始化,类加载。
    • 问题2:无,类加载由 jvm 进行,线程安全。
    更多相关内容
  • 下面小编就为大家带来一篇老生常谈C++的单例模式与线程安全单例模式(懒汉/饿汉)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  •  需要用锁,来保证其线程安全性:原因:多个线程可能进入判断是否已经存在实例的if语句,从而non thread safety.  使用double-check来保证thread safety.但是如果处理大量数据时,该锁才成为严重的性能瓶颈。 ...
  • 保证一个类只有一个实例,并提供一个访问它的全局访问点,使得系统中只有唯一的一个对象实例,具有线程安全,多线程测试通过。 1.打开日志并创建日志文件夹 默认为程序启动路径 2.清理日志文件下日志数量 默认保留90...
  • C++11实现线程安全单例代码和测试代码,包含singleton.h,main.cpp,希望帮助到大家。
  • 主要介绍了python实现线程安全单例模式,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 使用"懒汉模式"与"饿汉模式"实现c++的单例模式,并且确保了单例模式的第一次实例化的线程安全,以及程序结束时,单例对象的资源收回,以防内存资源的泄漏
  • 在某些应用环境下面,一个类只允许有一个实例,这就是著名的单例模式单例模式分为懒汉模式,跟饿汉模式两种。 首先给出饿汉模式的实现 正解: template class singleton { protected: singleton(){}; private: ...
  • 什么是线程安全? 在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。 如何保证线程安全? 给共享的资源加把锁,保证每...

    什么是线程安全?

    在拥有共享数据多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。


    如何保证线程安全?

    1. 共享的资源加把,保证每个资源变量每时每刻至多被一个线程占用。
    2. 让线程也拥有资源,不用去共享进程中的资源。如: 使用threadlocal可以为每个线程的维护一个私有的本地变量。

    什么是单例模式?

    单例模式指在整个系统生命周期里,保证一个类只能产生一个实例,确保该类的唯一性

    单例模式分类

    单例模式可以分为懒汉式饿汉式,两者之间的区别在于创建实例的时间不同

    • 懒汉式:指系统运行中,实例并不存在,只有当需要使用该实例时,才会去创建并使用实例。(这种方式要考虑线程安全)
    • 饿汉式:指系统一运行,就初始化创建实例,当需要时,直接调用即可。(本身就线程安全,没有多线程的问题)

    单例类特点

    • 构造函数和析构函数为private类型,目的禁止外部构造和析构
    • 拷贝构造和赋值构造函数为private类型,目的是禁止外部拷贝和赋值,确保实例的唯一性
    • 类里有个获取实例的静态函数,可以全局访问

    01 普通懒汉式单例 ( 线程不安全 )

    ///  普通懒汉式实现 -- 线程不安全 //
    #include <iostream> // std::cout
    #include <mutex>    // std::mutex
    #include <pthread.h> // pthread_create
    
    class SingleInstance
    {
    
    public:
        // 获取单例对象
        static SingleInstance *GetInstance();
    
        // 释放单例,进程退出时调用
        static void deleteInstance();
    	
    	// 打印单例地址
        void Print();
    
    private:
    	// 将其构造和析构成为私有的, 禁止外部构造和析构
        SingleInstance();
        ~SingleInstance();
    
        // 将其拷贝构造和赋值构造成为私有函数, 禁止外部拷贝和赋值
        SingleInstance(const SingleInstance &signal);
        const SingleInstance &operator=(const SingleInstance &signal);
    
    private:
        // 唯一单例对象指针
        static SingleInstance *m_SingleInstance;
    };
    
    //初始化静态成员变量
    SingleInstance *SingleInstance::m_SingleInstance = NULL;
    
    SingleInstance* SingleInstance::GetInstance()
    {
    
    	if (m_SingleInstance == NULL)
    	{
    		m_SingleInstance = new (std::nothrow) SingleInstance;  // 没有加锁是线程不安全的,当线程并发时会创建多个实例
    	}
    
        return m_SingleInstance;
    }
    
    void SingleInstance::deleteInstance()
    {
        if (m_SingleInstance)
        {
            delete m_SingleInstance;
            m_SingleInstance = NULL;
        }
    }
    
    void SingleInstance::Print()
    {
    	std::cout << "我的实例内存地址是:" << this << std::endl;
    }
    
    SingleInstance::SingleInstance()
    {
        std::cout << "构造函数" << std::endl;
    }
    
    SingleInstance::~SingleInstance()
    {
        std::cout << "析构函数" << std::endl;
    }
    ///  普通懒汉式实现 -- 线程不安全  //
    
    // 线程函数
    void *PrintHello(void *threadid)
    {
        // 主线程与子线程分离,两者相互不干涉,子线程结束同时子线程的资源自动回收
        pthread_detach(pthread_self());
    
        // 对传入的参数进行强制类型转换,由无类型指针变为整形数指针,然后再读取
        int tid = *((int *)threadid);
    
        std::cout << "Hi, 我是线程 ID:[" << tid << "]" << std::endl;
    
        // 打印实例地址
        SingleInstance::GetInstance()->Print();
    
        pthread_exit(NULL);
    }
    
    #define NUM_THREADS 5 // 线程个数
    
    int main(void)
    {
        pthread_t threads[NUM_THREADS] = {0};
        int indexes[NUM_THREADS] = {0}; // 用数组来保存i的值
    
        int ret = 0;
        int i = 0;
    
        std::cout << "main() : 开始 ... " << std::endl;
    
        for (i = 0; i < NUM_THREADS; i++)
        {
            std::cout << "main() : 创建线程:[" << i << "]" << std::endl;
            
    		indexes[i] = i; //先保存i的值
    		
            // 传入的时候必须强制转换为void* 类型,即无类型指针
            ret = pthread_create(&threads[i], NULL, PrintHello, (void *)&(indexes[i]));
            if (ret)
            {
                std::cout << "Error:无法创建线程," << ret << std::endl;
                exit(-1);
            }
        }
    
        // 手动释放单实例的资源
        SingleInstance::deleteInstance();
        std::cout << "main() : 结束! " << std::endl;
    	
        return 0;
    }
    

    普通懒汉式单例运行结果:

    从运行结果可知,单例构造函数创建了两个,内存地址分别为0x7f3c980008c00x7f3c900008c0,所以普通懒汉式单例只适合单进程不适合多线程,因为是线程不安全的。


    02 加锁的懒汉式单例 ( 线程安全 )

    ///  加锁的懒汉式实现  //
    class SingleInstance
    {
    
    public:
        // 获取单实例对象
        static SingleInstance *&GetInstance();
    
        //释放单实例,进程退出时调用
        static void deleteInstance();
    	
        // 打印实例地址
        void Print();
    
    private:
        // 将其构造和析构成为私有的, 禁止外部构造和析构
        SingleInstance();
        ~SingleInstance();
    
        // 将其拷贝构造和赋值构造成为私有函数, 禁止外部拷贝和赋值
        SingleInstance(const SingleInstance &signal);
        const SingleInstance &operator=(const SingleInstance &signal);
    
    private:
        // 唯一单实例对象指针
        static SingleInstance *m_SingleInstance;
        static std::mutex m_Mutex;
    };
    
    //初始化静态成员变量
    SingleInstance *SingleInstance::m_SingleInstance = NULL;
    std::mutex SingleInstance::m_Mutex;
    
    SingleInstance *&SingleInstance::GetInstance()
    {
    
        //  这里使用了两个 if判断语句的技术称为双检锁;好处是,只有判断指针为空的时候才加锁,
        //  避免每次调用 GetInstance的方法都加锁,锁的开销毕竟还是有点大的。
        if (m_SingleInstance == NULL) 
        {
            std::unique_lock<std::mutex> lock(m_Mutex); // 加锁
            if (m_SingleInstance == NULL)
            {
                m_SingleInstance = new (std::nothrow) SingleInstance;
            }
        }
    
        return m_SingleInstance;
    }
    
    void SingleInstance::deleteInstance()
    {
        std::unique_lock<std::mutex> lock(m_Mutex); // 加锁
        if (m_SingleInstance)
        {
            delete m_SingleInstance;
            m_SingleInstance = NULL;
        }
    }
    
    void SingleInstance::Print()
    {
    	std::cout << "我的实例内存地址是:" << this << std::endl;
    }
    
    SingleInstance::SingleInstance()
    {
        std::cout << "构造函数" << std::endl;
    }
    
    SingleInstance::~SingleInstance()
    {
        std::cout << "析构函数" << std::endl;
    }
    ///  加锁的懒汉式实现  //
    

    加锁的懒汉式单例的运行结果:

    从运行结果可知,只创建了一个实例,内存地址是0x7f28b00008c0,所以加了互斥锁的普通懒汉式是线程安全的


    03 内部静态变量的懒汉单例(C++11 线程安全)

    ///  内部静态变量的懒汉实现  //
    class Single
    {
    
    public:
        // 获取单实例对象
        static Single &GetInstance();
    	
    	// 打印实例地址
        void Print();
    
    private:
        // 禁止外部构造
        Single();
    
        // 禁止外部析构
        ~Single();
    
        // 禁止外部复制构造
        Single(const Single &signal);
    
        // 禁止外部赋值操作
        const Single &operator=(const Single &signal);
    };
    
    Single &Single::GetInstance()
    {
        // 局部静态特性的方式实现单实例
        static Single signal;
        return signal;
    }
    
    void Single::Print()
    {
    	std::cout << "我的实例内存地址是:" << this << std::endl;
    }
    
    Single::Single()
    {
        std::cout << "构造函数" << std::endl;
    }
    
    Single::~Single()
    {
        std::cout << "析构函数" << std::endl;
    }
    ///  内部静态变量的懒汉实现  //
    

    内部静态变量的懒汉单例的运行结果:

    -std=c++0x编译是使用了C++11的特性,在C++11内部静态变量的方式里是线程安全的,只创建了一次实例,内存地址是0x6016e8,这个方式非常推荐,实现的代码最少!

    [root@lincoding singleInstall]#g++  SingleInstance.cpp -o SingleInstance -lpthread -std=c++0x
    


    04 饿汉式单例 (本身就线程安全)

    // 饿汉实现 /
    class Singleton
    {
    public:
        // 获取单实例
        static Singleton* GetInstance();
    
        // 释放单实例,进程退出时调用
        static void deleteInstance();
        
        // 打印实例地址
        void Print();
    
    private:
        // 将其构造和析构成为私有的, 禁止外部构造和析构
        Singleton();
        ~Singleton();
    
        // 将其拷贝构造和赋值构造成为私有函数, 禁止外部拷贝和赋值
        Singleton(const Singleton &signal);
        const Singleton &operator=(const Singleton &signal);
    
    private:
        // 唯一单实例对象指针
        static Singleton *g_pSingleton;
    };
    
    // 代码一运行就初始化创建实例 ,本身就线程安全
    Singleton* Singleton::g_pSingleton = new (std::nothrow) Singleton;
    
    Singleton* Singleton::GetInstance()
    {
        return g_pSingleton;
    }
    
    void Singleton::deleteInstance()
    {
        if (g_pSingleton)
        {
            delete g_pSingleton;
            g_pSingleton = NULL;
        }
    }
    
    void Singleton::Print()
    {
        std::cout << "我的实例内存地址是:" << this << std::endl;
    }
    
    Singleton::Singleton()
    {
        std::cout << "构造函数" << std::endl;
    }
    
    Singleton::~Singleton()
    {
        std::cout << "析构函数" << std::endl;
    }
    // 饿汉实现 /
    

    饿汉式单例的运行结果:

    从运行结果可知,饿汉式在程序一开始就构造函数初始化了,所以本身就线程安全的

    展开全文
  • 线程安全单例模式

    千次阅读 2022-07-07 20:51:46
    单例模式实现, Java实现线程安全单例模式, 单例模式创建方法, 单例模式使用指南

    单例模式,即我们只允许一个类有且仅有一个实例对外提供服务。通常为了性能考虑,单例模式会以懒加载的形式创建,也即单例中的懒汉模式,与之相对的当然就是单例的饿汉模式:不管用不用,我可以先给你创建出来。

    1.单线程下的单例模式实现

    非多线程环境下,由于不存在线程对共享数据(对象)的竞争,所以也就没有线程安全问题,其单例模式实现如下:

    public final class FooSingleton {
    
        //持有类的唯一实例
        private static FooSingleton instance = null;
    
        /**
         * 私有构造函数
         */
        private FooSingleton() {
            //do nothing
        }
    
        /**
         * 提供外部获取实例的工厂方法
         * 非线程安全
         * @return
         */
        private static FooSingleton getInstance(){
            if (null == instance) {//非线程安全
                instance = new FooSingleton();
            }
            return instance;
        }
    
        public void methodOther() {
            //类提供的实例方法,调用方获取单例对象后,可调用类中的实例方法
        }
    
    }
    

    2.多线程下的单例模式实现

    可以发现,在多线程环境下,这个获取单例的方法不是线程安全的:

    private static FooSingleton getInstance(){
            if (null == instance) {//非线程安全
                instance = new FooSingleton();
            }
            return instance;
        }
    

    这就会导致客户端(也就是各个调用端)获取到的对象并不是同一个对象,这显然就违背了我们使用单例模式的初衷了。怎样保证线程安全呢?常用的实现方法有如下三种:

    2-1 直接加锁实现线程安全的单例模式

    直接加锁实现线程安全的单例模式:

    public final class FooSingletonSafety {
    
        //持有类的唯一实例
        private static FooSingletonSafety instance = null;
    
        /**
         * 私有构造函数
         */
        private FooSingletonSafety() {
            //do nothing
        }
    
        /**
         * 提供外部获取实例的工厂方法
         * 线程安全:加锁实现
         * synchronized保障了原子性、可见性和有序性
         * @return
         */
        private static FooSingletonSafety getInstance(){
            synchronized (FooSingletonSafety.class) {
                if (null == instance) {
                    instance = new FooSingletonSafety();
                }
            }
    
            return instance;
        }
    
        public void methodOther() {
            //类提供的实例方法,调用方获取单例对象后,可调用类中的实例方法
        }
    
    }
    

    显然,直接加锁是可以保证单例模式的线程安全的,但是你可能会发现,这种实现方式下,每个线程在获取单例对象的时候都要去抢锁,获取之后再释放锁,这显然效率是不高的(每次获取对象都要有锁开销)。所以,一般我们会通过下面的DCL去实现线程安全的单例模式

    2-2 DCL实现线程安全的单例模式

    DCL,即double-check-locking双重检查锁定,代码实现如下:

    public final class FooSingletonBasedDCL {
    
        //持有类的唯一实例
        private static volatile FooSingletonBasedDCL instance = null;
    
        /**
         * 私有构造函数
         */
        private FooSingletonBasedDCL() {
            //do nothing
        }
    
        /**
         * 提供外部获取实例的工厂方法
         * 线程安全:DCL实现
         * synchronized保障了原子性、可见性和有序性
         * volatile保证校验时(null == instance) instance对各个线程是可见的,同时禁止JIT和处理器重排序
         * @return
         */
        private static FooSingletonBasedDCL getInstance(){
            if (null == instance) {
                synchronized (FooSingletonSafety.class) {
                    if (null == instance) {
                        //new关键字对应三条指令:1.分配对象所需的存储空间  2.实例化对象  3.将对象引用赋值给变量
                        //这三条指令正常执行顺序是1->2->3,但是JIT编译器或处理器在执行时可能会将其优化为1->3->2
                        //所以,为了保证instance的可见性以及禁止重排序,所以instance变量必须要用volatile修饰
                        //否则,极端情况下,一个线程在判断if (null == instance)时,instance不为空直接返回,但此时
                        //instance可能是个半对象(对象并没有经过构造方法初始化),这将会造成代码错误
                        instance = new FooSingletonBasedDCL();
                    }
                }
            }
    
            return instance;
        }
    
        public void methodOther() {
            //类提供的实例方法,调用方获取单例对象后,可调用类中的实例方法
        }
    
    }
    

    DCL你可能开发中基本没用过,但是这种实现方式在很多开源框架如Spring、dubbo等中都有使用

    2-3 利用类加载机制实现线程安全的单例模式

    利用Java的类加载机制:加载->链接->初始化,我们可以轻易的实现线程安全的单例模式,这种单例模式形式最简单,代码实现如下:

    public final class BarSingleton {
        //利用类初始化只会发生一次,创建线程安全的单例
        private static final BarSingleton INSTANCE = new BarSingleton();
    
        public static BarSingleton getInstance() {
            return INSTANCE;
        }
    
        public void methodOther() {
            //类提供的实例方法,调用方获取单例对象后,可调用类中的实例方法
        }
    }
    
    展开全文
  • 线程安全单例模式C++实现

    千次阅读 2022-01-21 22:48:22
    在使用过程中,写一个具有单例模式思想的程序可以很简单,但是如果要做到线程安全并且生成实例的过程中符合业务需求,是一件困难的事情。 Lazy mode(懒汉模式) 懒汉模式,会将实例化的时间延迟到正式调用接口的...

    单例模式的介绍

    单例模式的优点是只有一个对象,可以节省资源,提高运行速度,提供一个全局的访问点。
    在使用过程中,写一个具有单例模式思想的程序可以很简单,但是如果要做到线程安全并且生成实例的过程中符合业务需求,是一件困难的事情。

    Lazy mode(懒汉模式)

    懒汉模式,会将实例化的时间延迟到正式调用接口的时候。
    简单的实现方式

    class singleton
    {
    public:
    	singleton* getInstance()
    	{
    		if(p_singletonIns == nullptr)
    	{
    	p_singletonIns = new singleton();
    }
    	return p_singletonIns;
    }
    
    private:
    	singleton()=default;
    	singleton(const singleton &cpSingleton) = delete;
    	singleton& operator=(const singleton& cpSingleton) = delete;
    	static singleton* p_singletonIns;
    };
    
    singleton* singleton::p_singletonIns = nullptr;
    

    上面的用例中,可以看到上面的p_singletonIns的在第一次调用getInstance()的时候会将p_singletonIns赋予一个实例的地址。这种延迟到使用时才进行实例化的做法,叫做懒汉模式。
    但是这种方法不是线程安全的,同时会存在内存泄露的问题。
    使用指针的方式的话,在创建实例的时候一般使用DCL(Double Check Lock), 双检测锁。代码如下所示

    class singleton
    {
    public:
    	singleton* getInstance()
    	{
    		if(p_singletonIns == nullptr) // p1
    		{
    			std::lock_guard<std::mutex> lock();
    			if(p_singletonIns == nullptr) // p2
    			{
    				p_singletonIns = new singleton(); // p3
    			}
    		}
    		return p_singletonIns;
    	}
    
    private:
    	singleton()=default;
    	singleton(const singleton &cpSingleton) = delete;
    	singleton& operator=(const singleton& cpSingleton) = delete;
    	static singleton* p_singletonIns;
    };
    
    singleton* singleton::p_singletonIns = nullptr;
    

    但是在使用过程中, p1的判断是存在一个风险,线程仍然不安全。就是在p3在new的时候,在new这个过程中,p_singletonIns的指针会被先赋值,然后开始创建内存。在并发的情况下,p1可能判断出p_singletonIns不是空指针,但是内存其实没有完全分配完成,那么造成返回p_singletonIns这个指针其实是不可用的。

    使用原子变量的懒汉模式

    在C++11的标准库中有原子变量的实现。使用原子变量就可以上述这种避免线程不安全的问题。

    std::mutex mutexIns;
    class singleton
    {
    public:
    static singleton* getInstance()
    {
    	if(p_singletonIns.load() == nullptr) // p1
    	{
    		std::lock_guard<std::mutex> lock(mutexIns);
    		if(p_singletonIns.load() == nullptr) // p2
    		{
    			p_singletonIns.store(new singleton()); // p3
    		}
    	}
    	return p_singletonIns.load();
    }
    
    private:
    	singleton()=default;
    	singleton(const singleton &cpSingleton) = delete;
    	singleton& operator=(const singleton& cpSingleton) = delete;
    	static std::atomic<singleton*> p_singletonIns;
    };
    std::atomic<singleton*> singleton::p_singletonIns(nullptr);
    

    使用局部静态变量的懒汉模式

    使用局部静态变量也可以实现懒汉模式,使用局部静态变量在C++11之后是线程安全。这也是最为优雅的一种方式。

    class singleton
    {
    public:
    	static singleton* getInstance()
    	{
    		singleton singletonIns;
    		return &singletonIns;
    	}
    
    private:
    	singleton()=default;
    	singleton(const singleton &cpSingleton) = delete;
    	const singleton& operator=(const singleton& cpSingleton) = delete;
    };
    

    饿汉模式

    饿汉模式:故名思意,一个饿坏的人会将一切能吃的东西都塞到肚子里,吃不下去的也要拽在手里。
    在编程中这种模式的体现就在于资源在程序启动的时候就会分配好。而不是在运行时才进行资源分配。
    使用饿汉模式编写单例模式相对简单一些。

    class singleton
    {
    public:
    	singleton* getInstance()
    	{
    		return &singletonIns;
    	}
    
    private:
    	singleton()=default;
    	singleton(const singleton &cpSingleton) = delete;
    	singleton& operator=(const singleton& cpSingleton) = delete;
    	static singleton singletonIns;
    };
    singleton singleton::singletonIns;
    

    使用饿汉模式的话,singletonIns在C++中进行初始化的过程发生在程序加载的过程中,在main运行之前,也在调用getInstance()之前。这个初始化的过程也可以知道是确保线程安全的。同时也可以保证在程序结束之后自动析构。

    关于资源泄露

    首先资源泄露并不只是意味着内存泄露,还会指网络资源的释放,比如在分布式系统中,在程序退出时,让其他系统知道程序的退出等等场景。
    在单例模式中,因为使用全局变量或者静态变量进行构造的时候,在程序退出时,会自动进行析构,通常是不存在资源泄漏的。在使用new的情况下,则不会自动进行析构,会存在资源泄漏的问题。下面介绍使用堆内存来构造单例模式的时候,如何在程序推出时,自动析构。

    使用静态的嵌套对象来进行释放

    使用嵌套的对象变量来释放,是因为单例模式的析构函数是private权限的,因此要使用嵌套对象来解决这个private的访问权限的问题。也满足高内聚编程原则。

    std::mutex mutexIns;
    class singleton
    {
    public:
    class deletor 
    {
    public:
        ~deletor()
        {
            singleton *singletonIns;
            if((singletonIns = getInstance()) != nullptr)
            {
                delete singletonIns;
            }
        }
    };
    
    static singleton* getInstance()
    {
    	if(p_singletonIns.load() == nullptr) // p1
    	{
    		std::lock_guard<std::mutex> lock(mutexIns);
    		if(p_singletonIns.load() == nullptr) // p2
    		{
    			p_singletonIns.store(new singleton()); // p3
    		}
    	}
    	return p_singletonIns.load();
    }
    	
    private:
    	singleton()=default;
    	singleton(const singleton &cpSingleton) = delete;
    	singleton& operator=(const singleton& cpSingleton) = delete;
    	~singleton()
        {
            //do anything to make sure you have prevented resource leak.
            //...
        }
    	static std::atomic<singleton*> p_singletonIns;
        static deletor   deletorIns;
    };
    std::atomic<singleton*> singleton::p_singletonIns(nullptr);
    singleton::deletor   deletorIns;
    
    展开全文
  • c++实现线程安全单例模式

    千次阅读 2022-04-27 13:44:40
    title: 单例模式 categories: [设计模式] tags: [单例模式] 我的私人博客 饿汉模式 #include <iostream> using namespace std; class HungrySingleton{ private: int count = 0; static HungrySingleton*...
  • python-线程安全单例模式

    千次阅读 2022-03-14 16:28:15
    *args, **kwargs): with CHGlobal._instance_lock: if not hasattr(CHGlobal, "_instance"): CHGlobal._instance = CHGlobal(*args, **kwargs) return CHGlobal._instance def test(self): print("CHGlobal单例测试...
  • namespace API { /// /// 业务逻辑API /// public partial class DesignAPI { private static DesignAPI... 用了两个if进行判断的原因还是在于多线程的特性,因为多线程访问函数时都是独立访问的,所以才要用到两个if;
  • 主要介绍了Java 单例模式线程安全问题的相关资料,希望通过本文大家能了解掌握单例模式线程安全的使用方法,需要的朋友可以参考下
  • 单例模式中的懒汉模式存在一定的线程安全,通过前面的线程安全处理可以应用到此处 public class BankTest { public static void main(String[] args) { Bank bank = Bank.getInstance(); System.out.println...
  • 第一种:饿汉模式(线程安全)publicclassSingle2{privatestaticSingle2instance=newSingle2();privateSingle2(){System.out.println("Single2:"+System.nanoTime());}publicstaticSingle2getInstance(){returnin...
  • C#中线程安全单例模式

    千次阅读 2020-09-10 10:21:08
    一、单例模式单例模式的定义:是用来保证这个类在运行期间只会被创建一个实例,并提供一个访问它的全局访问点。 ②单例模式的作用:保证一个类只有一个访问实例,节省系统资源。 ③单例模式的特点:私有构造...
  • 1、饿汉模式 //饿汉模式:(线程安全) public class Singleton { // 静态私有成员变量 private static Singleton instance = new Singleton(); // 私有构造函数 private Singleton() { } // 静态方法,返回唯一实例...
  • 深入浅出:讲解单例模式,多线程安全和并发访问问题.让你轻松应对面试
  • 饿汉式单例模式是天然的线程安全,在类加载时实例化对象 public class Singleton { private static Singleton instance = new Singleton(); private Singleton() {} public static Singleton getInstance()...
  • 线程安全单例模式实现有几种思路,个人认为第2种方案最优雅:、饿汉式、借助内部类、普通加锁解决、双重检测,但要注意写法,如果单体模式继续扩展为N元单体模式,那就是对象池模式了
  • C++11规定了local static在多线程条件下的初始化行为,要求编译器保证了内部静态变量的线程安全性。在C++11标准下,《Effective C++》提出了一种更优雅的单例模式实现,使用函数内的 local static 对象。这样,只有...
  • 主要为大家详细介绍了java多线程之线程安全单例模式,文章内容全面,感兴趣的小伙伴们可以参考一下
  • 主要介绍了Java线程安全中的单例模式,需要的朋友可以参考下
  • 单例模式: 饿汉式(线程安全) //单例模式: 饿汉式 线程安全 class SingleTemple{ // 1.私有化构造器 private SingleTemple(){} // 2.创建私有对象 private static SingleTemple instance = new SingleTemple...
  • 单例模式:意思是一个类只能实例化一个对象,且无法通过拷贝构造,赋值构造等方法构造对象。 线程安全模式 饥汉模式 懒汉模式

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 123,517
精华内容 49,406
关键字:

线程安全单例模式

友情链接: travtose.rar