创建型之单例模式
前言
单例模式随处可见,用处也很大,但是写好一个单例模式很不容易,拔出萝卜带出泥,面试也经常会被问到,今天就来总结一下。
什么是单例?
- 单例类只能有1个实例对象,
- 单例类必须自己创建该实例,别人不能创建
- 单例类必须该所有其他对象提供该实例
1. 饥饿式
/**
* 1-饿汉式单例类
*/
public class EagerSingleton {
//private保证实例不被直接访问,static保证只有1份,final保证这1份不被修改
private static final EagerSingleton instance = new EagerSingleton();
//private保证不被其它类new出来
private EagerSingleton(){}
//静态工厂方法,给其它对象提供相同方法
public static EagerSingleton getInstance(){
return instance;
}
}
优点:在类被加载的时候,就创建了实例,多线程下是安全的。
缺点:创建单例的时机过于急躁,在没有用到单例之前,占用了内存,而且初始化加载的配置到最后可能并不是我们想要的
2. 懒汉式
/**
* 2-懒汉式单例模式
*/
public class LazySingleton {
//当getInstance方法被调用时才会被创建,没有final修饰,不能保证线程安全
private static LazySingleton instance = null;
private LazySingleton(){}
public synchronized static LazySingleton getInstance() {
if(instance == null){
//在多线程情况下,实例的创建会出现现不一致情况,因此要加synchronized
instance = new LazySingleton();
}
return instance;
}
}
优点:保证了线程安全,只有在被用到的情况下,才会初始化实例,节省了空间
缺点:synchronized加在了方法上,同步过度,每个线程执行getInstance方法时都要同步,执行效率比较低,最好的情况是只在实例第一次初始化时,执行同步
3. 双重检查锁
/**
* 3-双重检查锁单例模式
*/
public class Singleton implements Serializable {
private static volatile Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
//如果实例已经被创建,则不被同步,这样可以提高执行效率
if (instance == null) {
//实例初次被创建时,可能有多个线程,所以要加锁,同步
synchronized (Singleton.class) {
//实例只可以被创建1次,等其它阻塞线程执行时,不在初始化
if (instance == null) {
//该赋值操作,只在当前线程的工作区中执行,什么时候会写到主存,由jvm执行,因此要加volatile,让其它线程可知
instance = new Singleton();
}
}
}
return instance;
}
//防被反序列化时创建多个实例对象
private Object readResolve() {
return getInstance();
}
//防止通过反射创建多个实例,重写getClass方法
private static Class getClass(String classname)
throws ClassNotFoundException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null)
classLoader = Singleton.class.getClassLoader();
return (classLoader.loadClass(classname));
}
}
优点:理想很丰满,思路清晰,只在初始化实例时,同步
缺点:现实很残酷,在java旧版本时,支持的并不很好,原因在于instance = new Singleton()在多线程情况下,可能出现构造方法还没执行,就被其它线程拿走了。现如今violate已被优化,可以实现,但是效率并不高
4. 静态内部类
/**
* 4-内部类单例模式
*/
public class InnerClassSingleton {
private static class SingletonHolder{
public static final InnerClassSingleton instance = new InnerClassSingleton();
}
public static InnerClassSingleton getInstance(){
return SingletonHolder.instance;
}
}
优点:由jvm保证线程安全,只有被用到时,才会被初始化
缺点:序列化和反序列化支持比较差
5. 枚举类
/**
* 5-枚举单例类
*/
public enum SingletonEnum {
INSTANCE; //单例实例
private DataSource dataSource;
private SingletonEnum(){
//初始化单例的内容,添加dataSource的配置内容
}
public Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
}
优点:最佳实践,完美解决各种问题
缺点:项目中运用的少
后记
能力有限,如有纰漏,请在评论区指出,老朽虽一把年纪,必当感激涕零,泪如雨下。若有满嘴喷粪撕逼者,一律拉黑、举报。
转载时,注明出处是人格的一种体现(https://www.zybuluo.com/BertLee/note/837611)。
撰笔之时,参考了以下几篇博文,如有不妥之处,请与老朽联系。
http://blog.csdn.net/huangyuan_xuan/article/details/52193006
http://blog.csdn.net/yangkai_hudong/article/details/50628172
http://www.cnblogs.com/java-my-life/archive/2012/03/31/2425631.html