单例模式
1、概述
单例模式是23种设计模式之一,也是最常用的设计模式。
就是系统运行期间,有且仅有一个实例。
例:
package cn.smbms.utils;
/**
* 单例类
* @author 14062
*
*/
public class Singleton {
//私有静态成员
private static Singleton singleton;
//私有构造
private Singleton(){}
//向外界提供获取实例的静态方法
public static Singleton getInstance(){
if(singleton==null){
singleton=new Singleton();
}
return singleton;
}
}
解: 必须满足单例模式的三个关键点:
- 一个类只能有一个实例(只能提供私有构造器,即保证不能随意创建该类的实例)
- 它必须自行创建这个实例。要保证唯一性,也就意味必须创建一个实例,那么就需要该类自行创建,以便向外界提供该类实例时使用。
- 它必须自行向整个系统提供这个实例。外界需要获取并使用这个单例类的实例,但是由于该类的构造器是私有的,外界无法通过new去获取它的实例,那么就必须提供一个静态的公有方法,该方法创建或者获取它本身私有对象并返回。
2、懒汉模式
概述:即在类加载时不创建实例,采用延迟加载的方式,在运行调用时创建实例。上面一段代码即为“懒汉模式”,此种方式虽然保持了其延迟加载的特性,但存在安全问题,在多线程下无法正常工作。如果需要解决线程安全的问题,最简单的方式就是使用synchronized关键字实现同步。
例:
//向外界提供获取实例的静态方法
public static synchronized Singleton getInstance(){
if(singleton==null){
singleton=new Singleton();
}
return singleton;
}
注: synchronized锁住的是类对象。懒汉模式虽然保留了延迟加载特性,但存在线程安全问题,所以使用synchronized关键字实现同步,但此种方式会降低效率,不推荐使用。
3、饿汉模式
概述:指在类加载的时候就完成了初始化操作,故类加载较慢,但是获取对象的速度很快,并且由于饿汉模式在类初始化时就已经自行实例化,因此不存在线程安全问题。
例:
//直接实例化
private static Singleton singleton=new Singleton();
//私有构造
private Singleton(){}
//向外界提供获取实例的静态方法
public static Singleton getInstance(){
return singleton;
}
注: 静态方法getInstance()直接返回已经自行实例化好的对象,这种方式基于classloader机制,有效的避免了多线程的同步问题。但是也有可能会有其他的地方(或者其他静态方法) 导致类装载,而此时单例类Singleton在类装载时就实例化,显然没有达到lazy loading的效果。在实际开发应用中,可能既需要lazy loading的效果,在效率与安全方面又有保证,这就需要用静态内部类来解决问题。
4、静态内部类
package cn.smbms.tools;
/**
* 单例类demo
* @author 14062
*
*/
public class Singleton {
private static Singleton singleton;
private Singleton(){
//在整个应用运行期间,只执行一次的业务操作代码(如读取配置文件的操作)
}
//静态内部类
public static class SingletonHelper{
private static final Singleton INSTANCE=new Singleton();
}
/*
* 只有通过调用getInstance()方法时,才会主动调用SingletonHelper类,
* 并完成对SingletonHelper的加载,从而实例化Singleton,达到lazy loading的效果。
*/
public static Singleton getInstance(){
singleton=SingletonHelper.INSTANCE;
return singleton;
}
public static Singleton test(){
return singleton;
}
}
5、小结
-
懒汉模式:类加载时不创建实例,因此类加载速度快,但是运行时获取对象的速度较慢,具备延迟加载的特性,但是存在线程不安全的问题。
-
饿汉模式:在类加载时就完成了初始化,所以类加载比较慢,但获取对象速度快。
-
静态内部类:即在单例类内部定义静态内部类,在静态内部类中完成对象的实例化操作,只有主动调用静态内部类时才会初始化实例,从而保证了线程的安全并达到延迟加载的效果。