• 【设计模式】单例模式 Singleton Pattern


    单例模式,或称作单件模式,在整个应用程序运行中只有一个实例并提供一个全局访问点。

    用途:整个程序只需要一个实例,eg.线程池、缓存、注册表、日志对象、打印机驱动等。

    如何实现单一实例,

    1)定义构造函数为private,禁止外部构造实例。

    2)提供static的Instance函数自己提供的实例。之所以定义为static是因为构造函数为private不能通过外部创建的实例访问,只能通过静态类方法调用Singleton::Instance()。而static本身也符合唯一实例的思想。

    C++实现1:

    Singleton.h

    #ifndef _SINGLETON_H_
    #define _SINGLETON_H_
    #include <iostream>
    
    class Singleton
    {
    public:
        static Singleton* Instance();
    private:
        Singleton();
        static Singleton* _instance;
    };
    #endif
    View Code

    Singleton.cpp

    #include "Singleton.h"
    #include "stdio.h"
    
    Singleton* Singleton::_instance = NULL;
    
    Singleton* Singleton::Instance()
    {
        if (_instance == NULL)
        {
            _instance = new Singleton();
        }
        return _instance;
    }
    
    Singleton::Singleton()
    {
        //todo something
    }
    View Code

    main.cpp

    #include "Singleton.h"
    
    int main(int argc,char* argv[])
    {
        Singleton* instance = Singleton::Instance();
        //todo something
        return 0;
    }
    View Code

    C++实现1问题:以上代码未考虑多线程:

    1)线程1线程2先后进入函数走到(几乎是同时)

    if (instance == NULL)

    2)线程1创建实例退出函数。

    3)线程2已经通过instance == NULL的判断,也创建实例。

    此时就创建出了2个实例,这明显违背了单一实例的原则。

    C++实现2:Lazy的实现

    Singleton.h

    #ifndef _SINGLETON_H_
    #define _SINGLETON_H_
    #include <iostream>
    
    class Singleton
    {
    public:
        static Singleton* Instance();
    private:
        Singleton();
        static Singleton* _instance;
    };
    #endif
    View Code

    Singleton.cpp

    #include "Singleton.h"
    #include "stdio.h"
    
    Singleton* Singleton::_instance = new Singleton();
    
    Singleton* Singleton::Instance()
    {
        return _instance;
    }
    
    Singleton::Singleton()
    {
        //todo something
    }
    View Code

    C++实现2问题:

    此种实现在进程启动时就创建出单一实例,后面不判断直接使用。但是如果Singleton存储数据很多,而应用场景很少,比如系统从开始到结束从来被调用过单例,那么这种不管三七二十一就创建的方式对系统资源是一个浪费。

    C++实现3:考虑多线程

    Singleton.cpp

    #include "Singleton.h"
    #include "stdio.h"
    
    Singleton* Singleton::_instance = NULL;
    
    Singleton* Singleton::Instance()
    {
        if (_instance == NULL)
        {
            mutex; //加锁-伪代码
                if (_instance == NULL)
                {
                    _instance = new Singleton();
                }
            mutex; //放锁-伪代码
        }
        return _instance;
    }
    
    Singleton::Singleton()
    {
        //todo something
    }
    View Code

    为什么要2次判空。

    如果一个加锁,一个判空:

    1)先加锁后判空,则每次调用Singleton::Instance()都要加锁,而只有第一次创建实例的时候才有可能重复创建实例,以后每次都加锁大大浪费性能。

    2)先判空后加锁,与之前没有锁是一样的,达不到防止重入的效果。

    需要注意的是,访问(增删查)单例类的数据时依然需要加锁。

    因为可能存在增删查同时进行的情况,而C++是通过迭代器访问数据,所以例如在线程1读的时候,线程2对数据进行了增删,则线程1的迭代器一经失效,可能因为程序崩溃。

    C#实现请参考汤姆大叔的神作:

    大叔手记(10):别再让面试官问你单例(暨6种实现方式让你堵住面试官的嘴)

    标准版实现:

     1     public sealed class Singleton
     2     {
     3         // 依然是静态自动hold实例
     4         private static volatile Singleton instance = null;
     5         // Lock对象,线程安全所用
     6         private static object syncRoot = new Object();
     7 
     8         private Singleton() { }
     9 
    10         public static Singleton Instance
    11         {
    12             get
    13             {
    14                 if (instance == null)
    15                 {
    16                     lock (syncRoot)
    17                     {
    18                         if (instance == null)
    19                             instance = new Singleton();
    20                     }
    21                 }
    22 
    23                 return instance;
    24             }
    25         }
    26     }
    View Code
  • 相关阅读:
    【转】myeclipse中连接mysql数据库
    struts2入门示例(hello world)
    【转】MyEclipse第一个Servlet程序
    学习马士兵的struts2/hibernate/spring中遇到的问题及其解决方法
    typeof关键字
    SHLVL--shell终端深度
    stack
    queue
    getopt--parse command line options
    怎样实时判断socket连接状态?
  • 原文地址:https://www.cnblogs.com/TonyZhao/p/3469207.html
Copyright © 2020-2023  润新知