一个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实例。