整理自汪文君老师著的《Java高并发编程详解》一书
Java 的 7种单例模式为:
- 饿汉式
- 懒汉式
- 懒汉式 + 同步方法
- Double-Check
- Volatile + Double-Check
- Holder方式
- 枚举方式
代码与特点如下
1. 饿汉式
public final class Singleton1 {
//实例变量
private Object data = new Object();
//在定义实例对象的时候直接初始化
private static Singleton1 instance = new Singleton1();
private Singleton1() { }
public static Singleton1 getInstance(){
return instance;
}
}
特点:
该模式下instance对象在类加载过程中就被初始化了,data变量会一开始就占用推内存,如果该单例类被ClassLoader加载很长时间后才被使用,就意味着刚开始分配的堆内存有些浪费。
饿汉模式可以保证多线程下的唯一实例,但无法实现懒加载。
2. 懒汉式
public final class Singleton2 {
private Object data = new Object();
private static Singleton2 instance = null;
private Singleton2() { }
public static Singleton2 getInstance() {
if(instance == null){
instance = new Singleton2();
}
return instance;
}
}
特点:
实现了懒加载,但不能保证多线程环境下instance的唯一性;比如当两个线程同时执行到 instance == null 时,这时instance还为null,那么都会接着进行instance == new Singleton2(),会造成两次实例化。
3. 懒汉式 + 同步方法
public final class Singleton3 {
private Object data = new Object();
private static Singleton3 instance = null;
private Singleton3() { }
// 加入同步控制,保证同一时间只能有一个线程进入该方法
public synchronized static Singleton3 getInstance() {
if (instance == null) {
instance = new Singleton3();
}
return instance;
}
}
特点:
对饿汉模式的升级版,能百分百保证instance实例的唯一性,但是因为同一时间只能由一个线程获得该实例,性能比较低。
4. Double-Check
public final class Singleton4 {
private Object data = new Object();
private List<String> list;
private Map<String,String> map;
private static Singleton4 instance = null;
private Singleton4(){
list = new ArrayList<>();
map = new HashMap<>();
}
public static Singleton4 getInstance() {
//当 instance 为 null 时,进入同步代码块。避免了每次调用 getInstance 方法时都必须同步,提高效率
if(instance == null){
//只能有一个线程能够获得Singleton.class关联的monitor
synchronized(Singleton4.class){
if(null == instance){
instance = new Singleton4();
}
}
}
return instance;
}
}
特点:
既满足了懒加载,又保证了 instance 的唯一性;但是 在多线程情况下可能或引起 空指针异常,第一个线程在执行 instance = new Singleton4()语句时,instance、list、map的实例化顺序不是一定的,可能instance先被实例化,list 和 map还没有被实例化,但这时另一个线程判断到 instance 不为空,那么直接返回 instance 了,这时如果直接调用了 instance的 list 对象进行 add 操作,将会造成空指针异常。
5. Volatile + Double-Check
public final class Singleton5 {
private Object data = new Object();
private List<String> list;
private Map<String, String> map;
//加入 volatile 关键字保证类成员变量的实例话发生在instance之前
private volatile static Singleton5 instance = null;
private Singleton5(){
list = new ArrayList<>();
map = new HashMap<>();
}
public static Singleton5 getInstance() {
// 当 instance 为 null 时,进入同步代码块。避免了每次调用 getInstance 方法时都必须同步,提高效率
if (instance == null) {
// 只能有一个线程能够获得Singleton.class关联的monitor
synchronized (Singleton5.class) {
if (null == instance) {
instance = new Singleton5();
}
}
}
return instance;
}
}
特点:
加入了 volatile 关键字,防止了空指针异常;实现了懒加载、多线程下的示例唯一性以及获取实例的高效性
6. Holder方式
public final class Singleton6 {
private Object data = new Object();
static {
System.out.println("这个类被实例化了");
}
private Singleton6() { }
//在静态内部类中持有Singleton的实力,并且可被直接初始化
private static class Holder{
private static Singleton6 instance = new Singleton6();
}
// 调用 getInstance 方法,事实上是获得Holder的instance静态属性
public static Singleton6 getInstance(){
return Holder.instance;
}
}
特点:
采用了内部类的方式,最好的单例设计方式之一,目前广为使用的设计之一
7. 枚举方式
public enum Singleton7 {
INSTASNCE;
private Object data = new Object();
Singleton7(){
System.out.println("INSTANCE will be initialized immediately");
}
public static void method(){
//调用该方法则会主动使用Singleton,INSTANCE 将会被初始化
}
public static Singleton7 getInstance(){
return INSTASNCE;
}
}
特点
线程安全且只能被实例化一次,但是枚举类型不能够懒加载,比如如果调用其中的静态方法,则INSTANCE会立即得到示例话
另外一种 | 算是Holder和枚举的组合体
public final class Singleton8 {
private Object data = new Object();
private Singleton8() { }
//使用枚举充当holder
private enum EnumHolder{
INSTANCE;
private Singleton8 instance;
EnumHolder(){
this.instance = new Singleton8();
}
private Singleton8 getSingleton(){
return instance;
}
}
public static Singleton8 getInstance(){
return EnumHolder.INSTANCE.getSingleton();
}
}