文章目录
1. 概念
如果一个类始终只能创建一个实例,则这个类被称为单例类,这种模式为单例模式。
注意两点:
- 将构造器私有化,不允许外界通过构造器创建对象;
- 通过公开的静态方法向外界返回类的逶迤实例。
2. 实现方式
2.1 单线程环境下懒汉式(不好)
public class Singleton{
private Singleton(){}
private static singleton instanse = null;
public static Singleton getInstance(){
if(instance==null){
instance=new Singleton;
}
return instance;
}
}
注解: Singleton
的静态属性instance
中,只有instance
为null
的时候才创建一个实例,构造函数私有,确保每次都只创建一个,避免重复创建。
**缺点:**只在单线程的情况下正常运行,在多线程的情况下,就会出问题。例如:当两个线程同时运行到判断instance
是否为空的if语句,并且instance
确实没有创建好时,那么两个线程都会创建一个实例。
2.2 多线程环境下懒汉式(不好)
public class Singleton{
private Singleton(){}
private static singleton instanse = null;
public static synchronized Singleton getInstance(){
if(instance==null){
instance=new Singleton;
}
return instance;
}
}
注解: 在解法一的基础上加上了同步锁
,使得在多线程的情况下可以用。例如:当两个线程同时想创建实例,由于在一个时刻只有一个线程能得到同步锁,当第一个线程加上锁以后,第二个线程只能等待。第一个线程发现实例没有创建,创建之。第一个线程释放同步锁,第二个线程才可以加上同步锁,执行下面的代码。由于第一个线程已经创建了实例,所以第二个线程不需要创建实例。保证在多线程的环境下也只有一个实例。
**缺点:**每次通过getInstance
方法得到singleton
实例的时候都有一个试图去获取同步锁的过程。而众所周知,加锁是很耗时的。能避免则避免。
2.3 加同步锁时,前后两次判断实例是否存在(可行)
public class Singleton{
private Singleton(){}
private static singleton instanse = null;
public static Singleton getInstance(){
if(instance==null){
synchronized(Singleton.class){
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
}
注解: 只有当instance
为null
时,需要获取同步锁,创建一次实例。当实例被创建,则无需试图加锁。
缺点: 用双重if判断,复杂,容易出错。
2.4 饿汉式(建议使用)
public class Singleton {
private static Singleton instance=new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
注解: 初试化静态的instance创建一次。如果我们在Singleton类里面写一个静态的方法不需要创建实例,它仍然会早早的创建一次实例。而降低内存的使用率。
缺点: 没有lazy loading
的效果,从而降低内存的使用率。
2.5 静态内部内(建议使用)
public class Singleton {
private Singleton(){}
public static class SingleHolder(){
private final static Singleton instance=new Singleton();
}
public static Singleton getInstance(){
return SingleHolder.instance;
}
}
注解: 定义一个私有的内部类,在第一次用这个嵌套类时,会创建一个实例。而类型为SingletonHolder的类,只有在Singleton.getInstance()中调用,由于私有的属性,他人无法使用SingleHolder,不调用Singleton.getInstance()
就不会创建实例。
优点: 达到了lazy loading
的效果,即按需创建实例。
值得注意的是:由于构造函数是私有的,因此上述所有方法不能被继承。
3. Spring bean中的单例模式
那么Spring对单例的底层实现,到底是饿汉式单例还是懒汉式单例呢?都不是。Spring框架对单例的支持是采用单例注册表
的方式进行实现的。首先,先了解单例注册表。
一个HashMap容器,以key-value形式存,获取的时候再在容器中取。
import java.util.HashMap;
public class RegSingleton {
//① 初始化一个HashMap,key存要单例生成的类名,value存以反射的形创的对象
private HashMap map = new HashMap();
static{
RegSingleton rs=new RegSingleton();
map.put(rs.getClass().getName(),rs);
}
//②获取单例的方法,参数为要获取的单例名。不传参,则为初始化中的;传参但map中没有,新建。最终从容器中返回。
//返回instance,并取名
public static RegSingleton getInstance(String name){
if(name=null){//没有参数名
name="RegSignleton";
}
if(map.get(name)==null){//传了参数名,但容器中没有
map.put(name,Class.forName(name).getClass().newInstance());//放在try-catch中
}
return (RegSingleton)map.get(name);
}
}
当Spring中配置单例模式(默认就是)的实例时,<bean id="date" class="java.util.Date" singleton="true"/>
,就会以注册表的方式(类似上述方式)生成实例。
大家真正要记住的是Spring对bean实例的创建是采用单例注册表的方式进行实现的,而这个注册表的缓存是HashMap对象,如果配置文件中的配置信息不要求使用单例,Spring会采用新建实例的方式返回对象实例。
附:Spring getBean的源码
public abstract class AbstractBeanFactory implements ConfigurableBeanFactory{
/**
* 充当了Bean实例的缓存,实现方式和单例注册表相同
*/
private final Map singletonCache=new HashMap();
public Object getBean(String name)throws BeansException{
return getBean(name,null,null);
}
...
public Object getBean(String name,Class requiredType,Object[] args)throws BeansException{
//对传入的Bean name稍做处理,防止传入的Bean name名有非法字符(或则做转码)
String beanName=transformedBeanName(name);
Object bean=null;
//手工检测单例注册表
Object sharedInstance=null;
//使用了代码锁定同步块,原理和同步方法相似,但是这种写法效率更高
synchronized(this.singletonCache){
sharedInstance=this.singletonCache.get(beanName);
}
if(sharedInstance!=null){
...
//返回合适的缓存Bean实例
bean=getObjectForSharedInstance(name,sharedInstance);
}else{
...
//取得Bean的定义
RootBeanDefinition mergedBeanDefinition=getMergedBeanDefinition(beanName,false);
...
//根据Bean定义判断,此判断依据通常来自于组件配置文件的单例属性开关
//<bean id="date" class="java.util.Date" scope="singleton"/>
//如果是单例,做如下处理
if(mergedBeanDefinition.isSingleton()){
synchronized(this.singletonCache){
//再次检测单例注册表
sharedInstance=this.singletonCache.get(beanName);
if(sharedInstance==null){
...
try {
//真正创建Bean实例
sharedInstance=createBean(beanName,mergedBeanDefinition,args);
//向单例注册表注册Bean实例
addSingleton(beanName,sharedInstance);
}catch (Exception ex) {
...
}finally{
...
}
}
}
bean=getObjectForSharedInstance(name,sharedInstance);
}
//如果是非单例,即prototpye,每次都要新创建一个Bean实例
//<bean id="date" class="java.util.Date" scope="prototype"/>
else{
bean=createBean(beanName,mergedBeanDefinition,args);
}
}
...
return bean;
}
}