前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >设计模式(二)单例模式Singleton(创建型)

设计模式(二)单例模式Singleton(创建型)

作者头像
黄规速
发布2022-04-14 18:55:34
2120
发布2022-04-14 18:55:34
举报
文章被收录于专栏:架构师成长之路

SINGLETON(单件)—对象创建型模式

几乎所有面向对象的程序中,总有一些类的对象需要是唯一的,例如,通过数据库句柄到数据库的连接是独占的。您希望在应用程序中共享数据库句柄,因为在保持连接打开或关闭时,它是一种开销。再如大家最经常用的IM,如QQ,在同一台电脑,一个帐号只能有唯一的登录。

一. 问题

怎样确保一个特殊类的实例是独一无二的(它是这个类的唯一实例),并且这个实例易于被访问呢?

二. 解决方案

1)全局变量:一个全局变量使得一个对象可以被访问,但它不能防止你实例化多个对象。因为你的任何代码都能修改全局变量,这将不可避免的引起更多调试的意外。换句话说,全局变量的状态总是会出现一些问题的。

2)类构造函数私有和类自身的静态方法:让类自身负责保存它的唯一实例(静态变量)。这个类可以保证没有其他实例可以被创建(通过截取创建新对象的请求) ,并且它可以提供一个访问该实例的方法(静态方法)。这就是Singleton模式。

三. 适用性

在下面的情况下可以使用单件模式 1)当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。

2)当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。

4. 实现:

UML结构:

代码:

代码语言:javascript
复制
class  Singleton {
	static  private Singleton instance = null;//静态成员保存唯一实例
	/**
	 * 私有构造函数,保证不能被外部访问
	 *
	 */
	private function Singleton() {} 
	/**
	 * 静态方法将创建这个实例的操作并保证只有一个实例被创建
	 *
	 * @return unknown
	 */
	public static Singleton getInstance() {
		if (instance == null ) {
			instance = new self();
		}
		return instance;
	}
}

5. 效果

Singleton模式有许多优点: 1) 对唯一实例的受控访问, 因为Singleton类封装它的唯一实例,所以它可以严格的控制客户怎样以及何时访问它。 2) 缩小名空间,Singleton模式是对全局变量的一种改进。它避免了那些存储唯一实例的全局变量污染名空间。 3) 允许对操作和表示的精化Singleton类可以有子类,而且用这个扩展类的实例来配置一个应用是很容易的。你可以用你所需要的类的实例在运行时刻配置应用。 4) 允许可变数目的实例 这个模式使得你易于改变你的想法,并允许Singleton类的多个实例。此外,你可以用相同的方法来控 制应用所使用的实例的数目。只有允许访问 Singleton实例的操作需要改变。

6 .单件模式可以多个实例

单件模式并不是说一个类只能只有一个实例。假设我们使用在一个web 请求或者进程里面。一个用户id对应的某个类只能有唯一的实例。在下面的例子中,我们的User类,可以有多个实例,每个实例对应一个uid. 实例列表注册到静态变量$_instance并和uid关联起来。最简单的例子是我们前面提到的QQ,在同一台电脑,可以使用多帐号登录, 但一个帐号只能有唯一的登录.

代码:

代码语言:javascript
复制
<?php
class  User {
	static  private  $_instance = array();//静态成员保存唯一实例
	private $_uid ;
	/**
	 * 私有构造函数,保证不能被外部访问
	 *
	 */
	private function __construct($uid ) {
		$this->_uid = $uid;
	} 
	/**
	 * 静态方法将创建这个实例的操作并保证只有一个实例被创建
	 *
	 * @return unknown
	 */
	public static function getInstance($uid = 0) {
		if (!self::$_instance || !isset(self::$_instance[$uid]) ) {
			self::$_instance[$uid] = new self($uid);
		}
		return self::$_instance[$uid];
	}
}

四、应用


单例模式的使用场景

开发工具类库中的很多工具类都应用了单例模式,比例线程池、缓存、日志对象等,它们都只需要创建一个对象。

单例模式的12种写法

1、饿汉式(静态变量)

代码语言:javascript
复制
public class Singleton {    
    private static Singleton instance = new Singletion(); 
   
    private Singletion() {    
    }    
    public static Singleton getInstance() {        
         return instance;    
    }
}

2、饿汉式(静态常量)

代码语言:javascript
复制
public class Singleton {    
     private final static Singleton INSTANCE = new Singleton();    
     
     private Singleton() {    
     }    

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

3、饿汉式(静态代码块)

代码语言:javascript
复制
public class Singleton {    
    private static Singleton instance;    
    static {        
       instance = new Singleton();    
    }    
    private Singleton() {    }    

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

上面三种写法本质上其实是一样的,也是各类文章在介绍饿汉式时常用的方式。但使用静态final的实例对象或者使用静态代码块依旧不能解决在反序列化、反射、克隆时重新生成实例对象的问题。

当单例对象有必要实现 Serializable 接口时,即使将其构造函数设为私有,在它反序列化时依然会通过特殊的途径再创建类的一个新的实例,相当于调用了该类的构造函数有效地获得了一个新实例!

4、 懒汉式(线程不安全)

代码语言:javascript
复制
public class Singleton {    
     private static Singleton instance;    
  
     private Singleton() {    }    
 
     public static Singleton getInstance() {        
         if (instance == null) {            
            instance = new Singleton();        
         }        
         return instance;    
    }   
}

5、懒汉式(线程安全,存在同步开销)

代码语言:javascript
复制
class Singleton {    
     private static Singleton intance = null;    
     private Singleton() {        
        //私有构造函数    }    

     public static synchronized Singleton getInstance()    {        
         if (intance == null) {            
             intance = new Singleton();        
         }        
         return intance;    
      }
}

6、懒汉式(线程假装安全,同步代码块)

代码语言:javascript
复制
class Singleton {    
     private static Singleton singleton;    
     private Singleton() {    }    

     public static Singleton getInstance() {        
          if (singleton == null) {            
            // 4.加入同步代码块            
                 synchronized (Singleton.class) {                
                     singleton = new Singleton();           
                 }       
           }        
           return singleton;    
    }
}

7、DCL「双重检测锁:Double Checked Lock」(假)

代码语言:javascript
复制
public class Singleton {    
     private static Singleton instance;    
     private Singleton() {    }    
     public static Singleton getInstance() {        
        if (instance == null) {            
           synchronized (Singleton.class) {                
               if (instance == null) {                    
                    instance = new Singleton();                
                }            
          }        
        }        
        return instance;    
     }
}

8、DCL「双重检测锁:Double Checked Lock」 单例(真)

volatile:

代码语言:javascript
复制
public class Singleton {    
    private static volatile Singleton instance;    
    private Singleton() {    }    
    public static Singleton getInstance() {        
        if (instance == null) {            
              synchronized (Singleton.class) {                
                  instance = new Singleton();            
              }        
         }        
         return instance;    
     }
}

9、 静态内部类(推荐使用)

代码语言:javascript
复制
public class Singleton {    
      private Singleton() {    }
    
      private static class SingletonInstance()    {        
          private static final Singleton INSTANCE = new Singleton();    
      }    

      public static Singleton getInstance() {        
          return SingletonInstance.INSTANCE;    
      }
}

3.10 枚举类单例模式

代码语言:javascript
复制
public enum Singleton {    
     INSTANCE;    private Resource instance;   
     Singleton() {        
         instance = new Resource();    
      }    
      public Resource getInstance() {        
         return instance;    
      }    
 
     public static class Resource {        
        private Resource() {        
     }   
 }
}

3.11 登记式单例--使用Map容器来管理单例模式

代码语言:javascript
复制
public class Singleton {    
     private static Map<String, Object> map = new HashMap<>();    
     public static void reglisterService(String key, Object instance) {        
        if (!map.containsKey) {            
             map.put(key, instance);        
        }    
     }    
     public static Object getInstance(String key) {        
         return map.get(key);    
      }
}

3.12 内部枚举类

代码语言:javascript
复制
public interface MySingleton {    
     void doSomething();
}
public enum Singleton implements MySingleton {    

         INSTANCE {        
            @Override        
            public void doSomething() {            
                 System.out.println("complete singleton");        
             }    
          };   
          public static MySingleton getInstance() {        
              return Singleton.INSTANCE;    
          }
}

4.Spring依赖注入对单例的使用

在AbstractBeanFactory中

代码语言:javascript
复制
@Override	public Object getBean(String name) throws BeansException {		return doGetBean(name, null, null, false);	}	@Override	public <T> T getBean(String name, Class<T> requiredType) throws BeansException {		return doGetBean(name, requiredType, null, false);	}	@Override	public Object getBean(String name, Object... args) throws BeansException {		return doGetBean(name, null, args, false);	}

4.1 doGetBean中getSingleton

代码语言:javascript
复制
/*
* 提示:该行代码过长,系统自动注释不进行高亮。一键复制会移除系统注释 
* protected <T> T doGetBean(			String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)			throws BeansException {		String beanName = transformedBeanName(name);		Object bean;		// Eagerly check singleton cache for manually registered singletons.		Object sharedInstance = getSingleton(beanName);		if (sharedInstance != null && args == null) {			if (logger.isTraceEnabled()) {				if (isSingletonCurrentlyInCreation(beanName)) {					logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +							"' that is not fully initialized yet - a consequence of a circular reference");				}				else {					logger.trace("Returning cached instance of singleton bean '" + beanName + "'");				}			}			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);		}		else {			// Fail if we're already creating this bean instance:			// We're assumably within a circular reference.			if (isPrototypeCurrentlyInCreation(beanName)) {				throw new BeanCurrentlyInCreationException(beanName);			}			// Check if bean definition exists in this factory.			BeanFactory parentBeanFactory = getParentBeanFactory();			if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {				// Not found -> check parent.				String nameToLookup = originalBeanName(name);				if (parentBeanFactory instanceof AbstractBeanFactory) {					return ((AbstractBeanFactory) parentBeanFactory).doGetBean(							nameToLookup, requiredType, args, typeCheckOnly);				}				else if (args != null) {					// Delegation to parent with explicit args.					return (T) parentBeanFactory.getBean(nameToLookup, args);				}				else if (requiredType != null) {					// No args -> delegate to standard getBean method.					return parentBeanFactory.getBean(nameToLookup, requiredType);				}				else {					return (T) parentBeanFactory.getBean(nameToLookup);				}			}			if (!typeCheckOnly) {				markBeanAsCreated(beanName);			}			StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")					.tag("beanName", name);			try {				if (requiredType != null) {					beanCreation.tag("beanType", requiredType::toString);				}				RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);				checkMergedBeanDefinition(mbd, beanName, args);				// Guarantee initialization of beans that the current bean depends on.				String[] dependsOn = mbd.getDependsOn();				if (dependsOn != null) {					for (String dep : dependsOn) {						if (isDependent(beanName, dep)) {							throw new BeanCreationException(mbd.getResourceDescription(), beanName,									"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");						}						registerDependentBean(dep, beanName);						try {							getBean(dep);						}						catch (NoSuchBeanDefinitionException ex) {							throw new BeanCreationException(mbd.getResourceDescription(), beanName,									"'" + beanName + "' depends on missing bean '" + dep + "'", ex);						}					}				}				// Create bean instance.				if (mbd.isSingleton()) {					sharedInstance = getSingleton(beanName, () -> {						try {							return createBean(beanName, mbd, args);						}						catch (BeansException ex) {							// Explicitly remove instance from singleton cache: It might have been put there							// eagerly by the creation process, to allow for circular reference resolution.							// Also remove any beans that received a temporary reference to the bean.							destroySingleton(beanName);							throw ex;						}					});					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);				}				else if (mbd.isPrototype()) {					// It's a prototype -> create a new instance.					Object prototypeInstance = null;					try {						beforePrototypeCreation(beanName);						prototypeInstance = createBean(beanName, mbd, args);					}					finally {						afterPrototypeCreation(beanName);					}					bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);				}				else {					String scopeName = mbd.getScope();					if (!StringUtils.hasLength(scopeName)) {						throw new IllegalStateException("No scope name defined for bean " + beanName + "'");					}					Scope scope = this.scopes.get(scopeName);					if (scope == null) {						throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");					}					try {						Object scopedInstance = scope.get(beanName, () -> {							beforePrototypeCreation(beanName);							try {								return createBean(beanName, mbd, args);							}							finally {								afterPrototypeCreation(beanName);							}						});						bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);					}					catch (IllegalStateException ex) {						throw new ScopeNotActiveException(beanName, scopeName, ex);					}				}			}			catch (BeansException ex) {				beanCreation.tag("exception", ex.getClass().toString());				beanCreation.tag("message", String.valueOf(ex.getMessage()));				cleanupAfterBeanCreationFailure(beanName);				throw ex;			}			finally {				beanCreation.end();			}		}		// Check if required type matches the type of the actual bean instance.		if (requiredType != null && !requiredType.isInstance(bean)) {			try {				T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);				if (convertedBean == null) {					throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());				}				return convertedBean;			}			catch (TypeMismatchException ex) {				if (logger.isTraceEnabled()) {					logger.trace("Failed to convert bean '" + name + "' to required type '" +							ClassUtils.getQualifiedName(requiredType) + "'", ex);				}				throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());			}		}		return (T) bean;	}
*/

4.2 getSingleton的实现

返回在给定名称下注册的(原始)单例对象,检查已经实例化的单例并允许提前 对当前创建的单例的引用(解析循环引用)。

代码语言:javascript
复制
public Object getSingleton(String beanName) {		return getSingleton(beanName, true);	}@Nullable	protected Object getSingleton(String beanName, boolean allowEarlyReference) {		// Quick check for existing instance without full singleton lock		Object singletonObject = this.singletonObjects.get(beanName);		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {			singletonObject = this.earlySingletonObjects.get(beanName);			if (singletonObject == null && allowEarlyReference) {				synchronized (this.singletonObjects) {					// Consistent creation of early reference within full singleton lock					singletonObject = this.singletonObjects.get(beanName);					if (singletonObject == null) {						singletonObject = this.earlySingletonObjects.get(beanName);						if (singletonObject == null) {							ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);							if (singletonFactory != null) {								singletonObject = singletonFactory.getObject();								this.earlySingletonObjects.put(beanName, singletonObject);								this.singletonFactories.remove(beanName);							}						}					}				}			}		}		return singletonObject;	}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022/04/09 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一. 问题
  • 二. 解决方案
  • 三. 适用性
  • 四、应用
    • 单例模式的12种写法
    • 4.Spring依赖注入对单例的使用
    相关产品与服务
    文件存储
    文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档