单例模式是设计模式中使用最多的模式之一,它是一种对象的建立模式,用于产生一个对象的实例,确保系统中一个类只有一个实例。在Java语言中,单例模式有如下好处:java
所以对于系统的关键组件和频繁使用的对象,能够设计为单例模式,减小系统的开销,提升系统性能。设计模式
单例模式的参与者不多,只有单例类和使用者,关系表以下:多线程
角色 | 做用 |
---|---|
单例类 | 提供单例的工厂,返回单例 |
使用者 | 获取并使用单例 |
类图以下:函数
单例模式的核心在于经过一个接口返回惟一实例化对象,简单实现以下:性能
/** * 单例模式--饿汉(没法作到延时加载) */
public class HungrySingleton {
private static HungrySingleton instance = new HungrySingleton();
/** * 私有构造方法 */
private HungrySingleton(){
System.out.println("建立了对象");
}
/** * 对外暴露的获取惟一实例的接口 * @return */
public static HungrySingleton getInstance(){
return instance;
}
/** * 序列化,反序列化保证单例 * @return */
private Object readResolve(){
return instance;
}
}
复制代码
原理:单例类必须私有化构造方法,保证不会在系统其余地方调用,其次instance成员变量和getInstance()方法必须是static修饰的。优化
注意:单例模式的这种实现方式很是简单,十分可靠,惟一不足是没法作到延时加载。假设单例的建立过程十分缓慢,因为instance的成员变量是由static修饰的,在JVM加载单例类的时候,单例对象就会存在,若是单例类还在系统中扮演别的角色,那么系统中任何使用单例类的地方都会初始化这个单例对象,而无论是否被用到。spa
为了解决上述问题,并提升系统在相关函数调用的反应速度,就须要加入延时加载机制,懒汉模式。线程
/** * 单例模式--懒汉(效率低,延时加载) */
public class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton() {
System.out.println("LazySingleton is create");
}
public static synchronized LazySingleton getInstance() {
if (instance == null) {
return new LazySingleton();
}
return instance;
}
}
复制代码
原理:首先,静态成员变量instance赋值为null,确保系统启动时没有额外负载,其次在getInstance()方法中判断当前单例instance对象是否存在,存在则返回,不存在建立单例对象。设计
注意:getInstance()必须是线程同步的,不然在多线程条件下,当线程1新建单例完成赋值前,线程2可能判断instance为null,线程2也建立了单例对象,致使多个实例被建立,所以同步关键字是必须的。使用上述单例模式的实现方式,虽然实现了延时加载,可是和第一种实现(饿汉)相比,引入了同步关键字,所以在多线程场景下,加载速度远远大于第一种实现方式,影响系统性能。code
继续改进,建立内部类:
/** * 使用内部类来维护单例的实例,当StaticSingleton被加载时候,内部类并无被初始化 * (instance并无被初始化),调用getInstance()才会被初始化。 */
public class StaticSingleton {
private StaticSingleton(){
System.out.println("StaticSingleton is create");
}
/** * 内部类,建立单例对象 */
private static class StaticSingleHolder{
private static StaticSingleton instance = new StaticSingleton();
}
public static StaticSingleton getInstance(){
return StaticSingleHolder.instance;
}
}
复制代码
原理:在这个实现中,使用内部类来维护单例实例,当StaticSingleton被加载的时候,内部类没有被初始化,能够确保StaticSingleton加载到JVM中,不会初始化单例类,当调用getInstance()时才会加载StaticSingleHolder,初始化instance对象,同时因为实例的创建是在类加载时完成的,对线程友好,getInstance()不须要使用同步关键字。
注意:使用内部类实现的单例,既能够实现延时加载也避免使用同步关键字,是比较完善的实现。可是若是经过反射机制强行调用私有构造方法,就会生成多个单例。同时序列化和反序列化可能破坏单例(饿汉代码readResolve()方法),场景很少见,若是存在,多加注意。
[^《Java性能程序优化 让你的Java程序更快、更稳定》 葛一鸣]