代码改变世界

设计模式之单例模式

2012-11-26 13:12  youxin  阅读(298)  评论(0编辑  收藏  举报

剖析经典的单例模式实现。

// NOTE: This is not thread safe!

public class Singleton {
    private static Singleton uniqueInstance;
 
    // other useful instance variables here
 
    private Singleton() {}
 
    public static Singleton getInstance() {
        if (uniqueInstance == null) {
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
 
    // other useful methods here
}

 

单例模式:

  确保一个类只有一个实例,并提供一个全局访问点。

上面的代码会造成多线程的灾难,处理多线程,只需要把getInstance()变成同步方法就可以了。

public  static synchronized Singleton getInstance() 


同步会降低性能。的确。而比你想象还有严重的一些问题是:只有第一次执行此方法时,才真正需要同步。换句话说,一旦设置好uniqueInstance变量,就不再需要同步这个方法了。之后每次调用这个方法,同步是一种累赘。

能够改善多线程吗?使单例模式线程安全!

1. 使用“急切”实例化,而不用延迟实例化的做法
饿汉式(推荐)与懒汉式


如果应用程序总是创建并使用单件实例,或者在创建和运行时方面的负担不太繁重,你可能想要急切eagerly创建此单件,如下所示:

public class Singleton{
private static Singleton uniqueInstance=new  Singleton();

private Singleton(){ }

public static Singleton getInstance()
{
    return uniqueInstance;
}
}

利用这个做法,我们依赖jvm在加载这个类时马上创建此唯一的单件实例。jvm保证在任何线程方法uniqueInstance静态变量之前,一定先创建此实例

 

2用“双重检查加锁”,在getInstance()中减少使用同步

利用双重检查加锁(double-checked locking),首先检查是否实例已经创建了,如果尚未创建,‘才’进行同步。这样一来,只有第一次同步,这正是我们想要的。

public class Singleton {
    private volatile static Singleton uniqueInstance;
 
    private Singleton() {}
 
    public static Singleton getInstance() {
        if (uniqueInstance == null) { 检查实例,如果不存在,就进入同步区块。
            synchronized (Singleton.class) { 注意,只有第一次才彻底执行这里的代码
                if (uniqueInstance == null) { 进入区块后,再检查一次,如果是null,才创建实例。
                    uniqueInstance = new Singleton();
                }
            }
        }
         return uniqueInstance;
    }
}

volatile关键词确保,当uniqueI变量被初始化成Singleton实例时,多个线程正确地处理uniqueInstance变量。

volatile用更低的代价代替同步

解释:同步的代价主要有覆盖范围决定,如果可以降低同步的覆盖范围,可大幅提升性能。

而volatile覆盖范围是变量级别的,因此同步代价很低。

volatile原理:告诉处理器,不要将其放入工作内存,而是直接在主存操作。因此,当多处理器或多线程在访问该变量时

都将直接操作主存,这在本质上做到了变量共享。

volation优势:

1.更大的程度吞吐量

2.更少的代码实现多线程

3.程序伸缩性好

4.比较好理解,无需太高的学习成本

volatile不足:

1.容易出问题

2.比较难设计

 

更多;http://coolshell.cn/articles/265.html

c++的单例模式

 

ACE vs Boost: Singleton的实现及点评

以前曾经讨论过Singleton的实现,这次在对照ACE和Boost代码的时候,又重新审视了一下二者对Singleton不同的实现。其间的差别也体现了不同的编程哲学:

ACE的实现更加偏重多线程中的安全和效率问题;Boost的实现则偏重于使用语言自身的特性满足Singleton模式的基本需求。

 

o ACE的实现

Douglas C. Schmidt在Double-Checked Locking: An Optimization Pattern for Efficiently Initializing and Accessing Thread-safe Objects一文中对double-check lock(一般译为双检锁)进行了详细的阐述。 

ACE的Singleton使用Adapter模式实现对其他类的适配,使之具有全局唯一的实例。由于C++标准并非明确指定全局静态对象的初始化顺序,ACE使用double-check lock保证线程安全,并使之不受全局静态对象初始化顺序的影响,同时也避免了全局静态实现方式的初始化后不使用的开销。

 

如果你能够准确的区分以下三种实现的弊端和隐患,对double-check lock也就有了足够的了解。

// -------------------------------------------
class Singleton
{
public:
    static Singleton *instance (void)
    {
        // Constructor of guard acquires
        // lock_ automatically.
        Guard<Mutex> guard (lock_);
        // Only one thread in the
        // critical section at a time.
        if (instance_ == 0)
            instance_ = new Singleton;
        return instance_;
        // Destructor of guard releases
        // lock_ automatically.
    }
private:
    static Mutex lock_;
    static Singleton *instance_;
}; 

// ---------------------------------------------
static Singleton *instance (void)
{
    if (instance_ == 0) {
        Guard<Mutex> guard (lock_);
        // Only come here if instance_
        // hasn’t been initialized yet.
        instance_ = new Singleton;
    }
    return instance_;


// ---------------------------------------------
class Singleton
{
public:
    static Singleton *instance (void)
    {
        // First check
        if (instance_ == 0)
        {
            // Ensure serialization (guard
            // constructor acquires lock_).
            Guard<Mutex> guard (lock_);
            // Double check.
            if (instance_ == 0)
                instance_ = new Singleton;
        }
        return instance_;
        // guard destructor releases lock_.
    }
private:
    static Mutex lock_;
    static Singleton *instance_;
};
我的:

#include<iostream>
#include<string>
#include"Mutex.h"
using namespace std;

using namespace muduo;

class Singleton{
public:
    static Singleton* getInstance()
    {
        //first check
        if(instance_ == NULL)
        {
            MutexLockGuard lock(mutex_);
            //double check
            if(instance_==NULL)
                instance_=new Singleton;
        }
        return instance_;
        
    }
private:
    Singleton(){ cout<<"A construct"<<endl;}
    ~Singleton( ) {cout<<"A destroy"<<endl; }
    
     static MutexLock mutex_;
    static Singleton* instance_;
};
Singleton* Singleton::instance_=NULL;
MutexLock Singleton::mutex_;

class A{
public:
    A(){cout<<"A construct"<<endl;}
    ~A(){cout<<"A destroy"<<endl;}
};
int main()
{
    Singleton::getInstance();
    Singleton::getInstance();
    
}
View Code

 大多数时候,这样的实现都不会出现问题。有经验的读者可能会问,pInstance指向的空间什么时候释放呢?更严重的问题是,该实例的析构函数什么时候执行?
如果在类的析构行为中有必须的操作,比如关闭文件,释放外部资源,那么上面的代码无法实现这个要求。我们需要一种方法,正常的删除该实例。
可以在程序结束时调用getInstance(),并对返回的指针掉用delete操作。这样做可以实现功能,但不仅很丑陋,而且容易出错。因为这样的附加代码很容易被忘记,而且也很难保证在delete之后,没有代码再调用getInstance函数。
一个妥善的方法是让这个类自己知道在合适的时候把自己删除,或者说把删除自己的操作挂在操作系统中的某个合适的点上,使其在恰当的时候被自动执行。
我们知道,程序在结束的时候,系统会自动析构所有的全局变量。事实上,系统也会析构所有的类的静态成员变量,就像这些静态成员也是全局变量一样。利用这个特征,我们可以在单例类中定义一个这样的静态成员变量,而它的唯一工作就是在析构函数中删除单例类的实例。如下面的代码中的Garbo类(Garbo意为垃圾工人):

    //内嵌类
    class Garbo
    {
        public:
            ~Garbo()
            {
                if(Singleton::instance_!=NULL)
                {
                    delete Singleton::instance_;
                }
                
            }
    };
private:
    Singleton(){ cout<<"A constructsdf"<<endl;}
    ~Singleton( ) {cout<<"A destroy"<<endl; }
    
    static Garbo garbo;
     static MutexLock mutex_;
    static Singleton* instance_;
};
Singleton* Singleton::instance_=NULL;
MutexLock Singleton::mutex_;
Singleton::Garbo Singleton::garbo;

类Garbo被定义为Singleton的私有内嵌类,以防该类被在其他地方滥用。
程序运行结束时,系统会调用Singleton的静态成员Garbo的析构函数,该析构函数会删除单例的唯一实例。
使用这种方法释放单例对象有以下特征:
在单例类内部定义专有的嵌套类;
在单例类内定义私有的专门用于释放的静态成员;
用程序在结束时析构全局变量的特性,选择最终的释放时机;
使用单例的代码不需要任何操作,不必关心对象的释放。

进一步的讨论

但是添加一个类的静态对象,总是让人不太满意,所以有人用如下方法来重现实现单例和解决它相应的问题,代码如下:
class Singleton
{
   //其他成员
   public:
       static Singleton &getInstance()
{
   static Singleton instance;
   return instance;
}
       private:
           Singleton() {};
};
使用局部静态变量,非常强大的方法,完全实现了单例的特性,而且代码量更少,也不用担心单例销毁的问题。
但使用此种方法也会出现问题,当如下方法使用单例时问题来了,
Singleton singleton = Singleton :: getInstance();
这么做就出现了一个类拷贝的问题,这就违背了单例的特性。产生这个问题原因在于:编译器会为类生成一个默认的构造函数,来支持类的拷贝。
最后没有办法,我们要禁止类拷贝和类赋值,禁止程序员用这种方式来使用单例,而是让getInstance()函数返回一个指针而不是返回一个引用,函数的代码改为如下:
static Singleton *getInstance()
{
static Singleton instance;
return &instance;
}
但我总觉的不好,为什么不让编译器不这么干呢。这时我才想起可以显示的生命类拷贝的构造函数,和重载 =操作符,新的单例类如下:
class Singleton
{
   //其他成员
   public:
       static Singleton &getInstance()
{
   static Singleton instance;
   return instance;
}
       private:
           Singleton() {};
           Singleton(const Singleton);
           Singleton & operate = (const Singleton&);
};
关于Singleton(const Singleton);和 Singleton & operate = (const Singleton&);函数,需要声明成私用的,并且只声明不实现。这样,如果用上面的方式来使用单例时,不管是在友元类中还是其他的,编译器都是报错。
不知道这样的单例类是否还会有问题,但在程序中这样子使用已经基本没有问题了。
(出处:http://snailbing.blogbus.com/logs/45398975.html)

饿汉式:

饿汉模式:即无论是否调用该类的实例,在程序开始时就会产生一个该类的实例,并在以后仅返回此实例。

由静态初始化实例保证其线程安全性,WHY?因为静态实例初始化在程序开始时进入主函数之前就由主线程以单线程方式完成了初始化,不必担心多线程问题。

故在性能需求较高时,应使用这种模式,避免频繁的锁争夺。

 class SingletonStatic
  {
  private:
      static const SingletonStatic* m_instance;
      SingletonStatic(){}
  public:
      static SingletonStatic* getInstance()
      {
          return m_instance;
     }
 };
 
 //外部初始化 before invoke main
 const SingletonStatic* SingletonStatic::m_instance = new SingletonStatic;

参考:http://blog.csdn.net/fu_zk/article/details/11892095


更多详情,见Schmidt的原文[as a resource in my space]和ACE_Singleton实现。

o Boost的实现 

Boost的Singleton也是线程安全的,而且没有使用锁机制。当然,Boost的Singleton有以下限制(遵从这些限制,可以提高效率): 

o The classes below support usage of singletons, including use in program startup/shutdown code, AS LONG AS there is only one thread running before main() begins, and only one thread running after main() exits. 

o This class is also limited in that it can only provide singleton usage for classes with default constructors. 

// T must be: no-throw default constructible and no-throw destructible
template <typename T>
struct singleton_default
{
private:
    struct object_creator
    {
        // This constructor does nothing more than ensure that instance()
        //  is called before main() begins, thus creating the static
        //  T object before multithreading race issues can come up.
        object_creator() { singleton_default<T>::instance(); }
        inline void do_nothing() const { }
    };
    static object_creator create_object; 

    singleton_default(); 

public:
    typedef T object_type; 

    // If, at any point (in user code), singleton_default<T>::instance()
    //  is called, then the following function is instantiated.
    static object_type & instance()
    {
        // This is the object that we return a reference to.
        // It is guaranteed to be created before main() begins because of
        //  the next line.
      static object_type obj; 

      // The following line does nothing else than force the instantiation
      //  of singleton_default<T>::create_object, whose constructor is
      //  called before main() begins.
      create_object.do_nothing(); 

      return obj;
    }
};
template <typename T>
typename singleton_default<T>::object_creator  // type of create_object is typenamed.
singleton_default<T>::create_object;     // c++ spec: initialization of static field of the singleton_default.


对于多数Singleton使用,Boost提供的版本完全能够满足需求。为了效率,我们有必要对其使用作出一定的限制。

 

 

 

而在多线程编程中,则有必要使用double-check lock降低频繁加锁带来的开销。

 

Additional---boost 的完整代码:

// Copyright (C) 2000 Stephen Cleary (shammah@voyager.net)
//
// This file can be redistributed and/or modified under the terms found
//  in "copyright.html"
// This software and its documentation is provided "as is" without express or
//  implied warranty, and with no claim as to its suitability for any purpose.
//
// See http://www.boost.org for updates, documentation, and revision history.

#ifndef BOOST_POOL_SINGLETON_HPP
#define BOOST_POOL_SINGLETON_HPP

// The following code might be put into some Boost.Config header in a later revision
#ifdef __BORLANDC__
# pragma option push -w-inl
#endif

//
// The following helper classes are placeholders for a generic "singleton"
//  class.  The classes below support usage of singletons, including use in
//  program startup/shutdown code, AS LONG AS there is only one thread
//  running before main() begins, and only one thread running after main()
//  exits.
//
// This class is also limited in that it can only provide singleton usage for
//  classes with default constructors.
//

// The design of this class is somewhat twisted, but can be followed by the
//  calling inheritance.  Let us assume that there is some user code that
//  calls "singleton_default<T>::instance()".  The following (convoluted)
//  sequence ensures that the same function will be called before main():
//    instance() contains a call to create_object.do_nothing()
//    Thus, object_creator is implicitly instantiated, and create_object
//      must exist.
//    Since create_object is a static member, its constructor must be
//      called before main().
//    The constructor contains a call to instance(), thus ensuring that
//      instance() will be called before main().
//    The first time instance() is called (i.e., before main()) is the
//      latest point in program execution where the object of type T
//      can be created.
//    Thus, any call to instance() will auto-magically result in a call to
//      instance() before main(), unless already present.
//  Furthermore, since the instance() function contains the object, instead
//  of the singleton_default class containing a static instance of the
//  object, that object is guaranteed to be constructed (at the latest) in
//  the first call to instance().  This permits calls to instance() from
//  static code, even if that code is called before the file-scope objects
//  in this file have been initialized.

namespace boost {

namespace details {
namespace pool {

// T must be: no-throw default constructible and no-throw destructible
template <typename T>
struct singleton_default
{
  private:
    struct object_creator
    {
      // This constructor does nothing more than ensure that instance()
      //  is called before main() begins, thus creating the static
      //  T object before multithreading race issues can come up.
      object_creator() { singleton_default::instance(); }
      inline void do_nothing() const { }
    };
    static object_creator create_object;

    singleton_default();

  public:
    typedef T object_type;

    // If, at any point (in user code), singleton_default<T>::instance()
    //  is called, then the following function is instantiated.
    static object_type & instance()
    {
      // This is the object that we return a reference to.
      // It is guaranteed to be created before main() begins because of
      //  the next line.
      static object_type obj;

      // The following line does nothing else than force the instantiation
      //  of singleton_default<T>::create_object, whose constructor is
      //  called before main() begins.
      create_object.do_nothing();

      return obj;
    }
};
template <typename T>
typename singleton_default<T>::object_creator
singleton_default<T>::create_object;

} // namespace pool
} // namespace details

} // namespace boost

// The following code might be put into some Boost.Config header in a later revision
#ifdef __BORLANDC__
# pragma option pop
#endif

#endif
 

@点评

Boost的singleton twisted code! disadvantage: lazy initialization or lazy load absence.

参考:http://blog.csdn.net/zirconsdu/article/details/6295163

 

http://blog.csdn.net/fullsail/article/details/8483106

http://blog.cnbang.net/tech/2229/

http://www.cnblogs.com/kex1n/archive/2011/04/05/2006194.html

http://blog.csdn.net/ajioy/article/details/8128032