解析Singleton开发模式

单例模式(Singleton pattern)
一个singleton 类是指这个类只能有一个实例,当系统只要求一个类的唯一实例时,我们就采用单例模式。要实现一个singleton类,我们首先要设置一个全局的点,以便其它类可以得到这个实例,其次很重要的一点是构造函数必须设定为私有,具体的实现通常有两种方式:饿汉式和懒汉式。从字面上去理解,懒汉式就是在不用的时候不去new这个实例,用到才去创建这个实例,它比较省空间,但是耗费时间;饿汗式是先提前new好实例,这样省时间但是耗费空间。

饿汉式:
public class Singleton {
	//设置全局点,在类加载的时候就实例化
	private static Singleton instence = new Singleton();
	//私有的构造函数
	private Singleton(){};
	public static Singleton getInstence(){
		return instence;
	}
}


懒汉式:
public class Singleton {
	//设置全局点,在类加载的时候仅仅声明一个空的对象
	private static Singleton instance = null;
	//私有的构造函数
	private Singleton(){};
	public static Singleton getInstence(){
		if(instence == null) 
			instence = new Singleton();
		return instence;
	}
}

对于饿汉式的singleton是线程安全的,因为它在类加载的时候就实例化了一个对象,在高并发的多线程中,能访问到的也只有这一个实例。而对于懒汉式来讲,它并不是线程安全的,我们可以想象,在一个多线程的环境下,线程a判断instence为空时,因为创建对象需要时间,所以线程a等待实例创建,这时线程b正好也判断instence,而这时instence仍然为空,所以线程b也导致new了一个对象,这样就导致创建了多个对象。于是我们想到了使用同步锁,我们可以把同步锁加在一个方法上面,也可以加在一段代码上面,在这里我们为了减少系统性能消耗,我们仅在创建对象的时候进行同步就可以,为了保证只有第一个线程才可以创建对象,我们要在判断一次instence是否为空,改进后的代码如下:
public class Singleton {
	//设置全局点,在类加载的时候仅仅声明一个空的对象
	private static Singleton instance = null;
	//私有的构造函数
	private Singleton(){};
	public static Singleton getInstence(){
		if(instence == null) 
			synchronized(Singleton.class){
				if(instence == null)
					instence = new Singleton();
			}
		return instence;
	}
}


***在这里我们用到了synchronized方法,如果对synchronized不了解,在其他文章中会详细介绍多线程中的同步机制。

但是我们如果仔细分析上面的例子,当创建对象的时候,jvm首先在栈中创建一个引用变量,然后再在堆中创建对象,中间是有时间间隔的,在这里,instence就是引用变量,jvm为它分配内存后,instence中的值已经不为空,这时如果另一个线程访问,它就会返回这个临时的值,因此用这种方法也不能解决线程安全问题。最终我们用了一个中间类解决了线程安全问题。
public class Singleton {
	private  Singleton() {}  
	private static class SingletonHolder {
		private static final Singleton INSTANCE = new Singleton();
	}
	public static Singleton getInstance() {
		 return SingletonHolder.INSTANCE; 
	}
}


当某对象第一次调用Singleton.getInstance()时,Singleton类被首次主动使用,jvm对其进行初始化(此时并不会调用Singleton()构造方法),然后Singleton调用getInstance()方法,该方法中,又首次主动使用了SingletonHolder类,所以要对SingletonHolder类进行初始化,初始化中,INSTANCE常量被赋值时才调用了Singleton的构造方法Singleton(),完成了实例化并返回该实例。当再有对象(也许是在别的线程中)再次调用Singleton.getInstance()时,因为已经初始化过了,不会再进行初始化步骤,所以直接返回INSTANCE常量即同一个Singleton实例。

猜你喜欢

转载自kickcode.iteye.com/blog/2262101