人生有涯,学海无涯
一、概述
单例模式(Singleton Pattern)是 Java 中最简略的设计模式之一。这种类型的设计模式属于创立型模式,它提供了一种创建对象的最佳形式。
这种模式波及到一个繁多的类,该类负责创立本人的对象,同时确保只有单个对象被创立。这个类提供了一种拜访其惟一的对象的形式,能够间接拜访,不须要实例化该类的对象。
留神:
- 1、单例类只能有一个实例。
- 2、单例类必须本人创立本人的惟一实例。
- 3、单例类必须给所有其余对象提供这一实例。
二、七种实现
2.1、懒汉式 — 线程不平安
是否 Lazy 初始化: 是
是否多线程平安: 否
实现难度: 易
形容: 这种形式是最根本的实现形式,这种实现最大的问题就是不反对多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
这种形式 lazy loading 很显著,不要求线程平安,在多线程不能失常工作。
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {if (instance == null) {instance = new Singleton();
}
return instance;
}
}
2.2、懒汉式 — 线程平安
是否 Lazy 初始化: 是
是否多线程平安: 是
实现难度: 易
形容: 这种形式具备很好的 lazy loading,可能在多线程中很好的工作,然而,效率很低,99% 状况下不须要同步。
长处:第一次调用才初始化,防止内存节约。
毛病:必须加锁 synchronized 能力保障单例,但加锁会影响效率。
getInstance() 的性能对应用程序不是很要害(该办法应用不太频繁)。
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {if (instance == null) {instance = new Singleton();
}
return instance;
}
}
2.3、饿汉式
是否 Lazy 初始化: 否
是否多线程平安: 是
实现难度: 易
形容: 这种形式比拟罕用,但容易产生垃圾对象。
长处:没有加锁,执行效率会进步。
毛病:类加载时就初始化,节约内存。
它基于 classloader 机制防止了多线程的同步问题,不过,instance 在类装载时就实例化,尽管导致类装载的起因有很多种,在单例模式中大多数都是调用 getInstance 办法,然而也不能确定有其余的形式(或者其余的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的成果。
public class Singleton {private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {return instance;}
}
2.4、饿汉式 — 变种
其实和之前的一样,仅仅是将单例的创立挪到了动态块。
public class Singleton {
private static Singleton instance;
static {instance = new Singleton();
}
private Singleton (){}
public static Singleton getInstance() {return instance;}
}
2.5、枚举式
JDK 版本:JDK1.5 起
是否 Lazy 初始化: 否
是否多线程平安: 是
实现难度: 易
形容: 这种实现形式还没有被宽泛采纳,但这是实现单例模式的最佳办法。它更简洁,主动反对序列化机制,相对避免屡次实例化。
这种形式是 Effective Java 作者 Josh Bloch 提倡的形式,它不仅能防止多线程同步问题,而且还主动反对序列化机制,避免反序列化从新创立新的对象,相对避免屡次实例化。不过,因为 JDK1.5 之后才退出 enum 个性,用这种形式写未免让人感觉陌生,在理论工作中,也很少用。
不能通过 reflection attack 来调用公有构造方法。
public enum Singleton {INSTANCE;}
2.6、双重校验锁式
JDK 版本:JDK1.5 起
是否 Lazy 初始化: 是
是否多线程平安: 是
实现难度: 较简单
形容: 这种形式采纳双锁机制,平安且在多线程状况下能放弃高性能。
getInstance() 的性能对应用程序很要害。
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {if (singleton == null) {synchronized (Singleton.class) {if (singleton == null) {singleton = new Singleton();
}
}
}
return singleton;
}
}
留神:
- volatile 的应用,为了避免裸露一个未初始化的不残缺单例实例;
- 双重判空校验,第一个判断防止了频繁的加锁,第二个判断能够拦住多余的创立实例的线程;
- 加锁,保障了线程平安(只有一个实例)
这种实现形式是经常出现在面试题中的,而且常常会要求手写。
2.7、动态外部类
是否 Lazy 初始化: 是
是否多线程平安: 是
实现难度: 个别
形容: 这种形式能达到双检锁形式一样的效用,但实现更简略。对动态域应用提早初始化,应应用这种形式而不是双检锁形式。这种形式只实用于动态域的状况,双检锁形式可在实例域须要提早初始化时应用。
public class Singleton {
private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {return SingletonHolder.INSTANCE;}
}
三、总结
下面列举的 7 种设计模式中第 1 种线程不平安,能够排除在外,第 3、4 种其实是一种,这样下来其实能够简化为 5 种形式:懒汉、饿汉、动态外部类、枚举、双重校验锁。