wbq813 Record Space

One who wants to wear the crown bear the crown.

ToC
designPattern-Singleton
/    

designPattern-Singleton

  • 动机
    在软件系统中有些特殊的类,必须保证只有一个实例,才能保证逻辑正确性以及良好的效率。如何绕过常规的构造器,来提供一种机制保证一个类只有一个实例。
  • 定义
    保证一个类仅有一个实例,并提供一个该实例的全局访问点。
  • C++实现
    	class Singleton{
    	private:
    		// S1. private constrator
    		Singleton();
    		Singleton(const Singleton& other);
    	public:
    		static Singleton* getInstance();
    		static Singleton* m_instance;
    	};
    	Singleton* Singleton::m_instance = nullptr;
    	// 1. not thread safe edition
    	Singleton* Singleton::getInstance(){
    		if(m_instance==nullptr){
    			// may be excute more than once in multi-thread
    			m_instance=new Singleton();
    		}
    	}
    	// 2. thread safe but lock not necessary after first new.
    	Singleton* Singleton::getInstance(){
    		Lock lock;
    		if(m_instance==nullptr){
    			m_instance=new Singleton();
    		}
    	}
    	// 3. Double check,not safe because of reorder;
    	Singleton* Singleton::getInstance(){
    		if(m_instance==nullptr){
    			Lock lock;
    			if(m_instance==nullptr){
    				m_instance=new Singleton();
    			}
    		}
    	}
    	// 4. thread safe after C++ 11
    	std::atomic<Singleton*> Singleton::m_instance;
    	std::mutex Singleton::m_mutes;
    
    	Singleton* Singleton::getInstance(){
    		Singleton* tmp=m_instance.load(std::memory_order_relaxed);
    		// get memory fence
    		std::atomic_thread_fence(std::memory_order_acquire);
    		if(m_instance==nullptr){
    			std::lock_guard<std::mutex> lock(m_mutex);
    			tmp=m_instance.load(std::memory_order_relaxed);
    			if(m_instance==nullptr){
    				// Release memory fence;
    				std::atomic_thread_fence(std::memory_order_release);
    				m_instance=new Singleton();
    			}
    		}
    		return tmp;
    	}
    

实现1中为非线程安全版本;实现2中在创建完成实例之后,频繁加锁性能浪费;实现3逻辑上正确,但是由于编译器指令重排导致不安全(某一线程可能拿到一个不等于nullptr但是没有执行构造函数的m_instance),在Java平台可以对m_instance 加上 volatile 使得整个赋值过程不能reorder; 实现4 利用内存屏障,防止被reorder;

  • reorder
    m_instance=new Singleton(); 这行代码逻辑上是先分配内存,再调用构造函数,再将内存地址复制给变量名;重排之后可能先申请内存,然后就复制给名称,最后再调用构造函数。
  • Java 实现
    eg. singleton in Spring-AOP
    	public final class GlobalAdvisorAdapterRegistry {
    		private GlobalAdvisorAdapterRegistry() {
    		}
    		private static AdvisorAdapterRegistry instance = new DefaultAdvisorAdapterRegistry();
    		public static AdvisorAdapterRegistry getInstance() {
    			return instance;
    		}
    		static void reset() {
    			instance = new DefaultAdvisorAdapterRegistry();
    		}
    	}
    
  • 总结
    1. 实例构造器可以设置为protected以允许子类派生;
    2. 一般不要支持拷贝函数和clone接口,因为可能导致多个对象实例;
      1.需要注意如何实现线程安全的singleton,注意检查双检查锁的正确实现。


Title: designPattern-Singleton
Author: wbq813
Traget: http://codeyourlife.cn/designPattern_Singleton

Comment