一、概念
单例模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。它确保一个类只有一个实例,并提供了一个全局访问点来访问该实例。
二、常用写法
1.饿汉模式
- 线程安全
- 不需要Lazy初始化
- 描述:容易产生垃圾对象
- 优点:没有锁,执行效率高
- 缺点:类加载时就初始化,浪费内存--容易产生垃圾对象
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
2.懒汉模式
- 线程安全
- Lazy初始化
- 描述:能够再多线程中很好的工作,但效率很低;99%的情况下不需要同步
- 优点:第一次调用才初始化,避免内存浪费
- 缺点:必须加synchronized锁,保证单例,但加锁会影响效率。
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
3.双重检测机制(DCL)
- 线程安全
- 需要Lazy初始化
- 描述:采用双锁机制,保证安全性,且在多线程情况下仍能够保持高性能
- 缺点:实现难度较大
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;
}
}
4.静态内部类
-
线程安全
- 需要Lazy初始化
- 描述:JVM将推迟SingletonHolder的初始化操作,直到开始使用这个类时才初始化,并且由于通过一个静态初始化来初始化Singleton,因此不需要额外的同步。当任何一个线程第一次调用getInstance时,都会使SingletonHolder被加载和被初始化,此时静态初始化器将执行Singleton的初始化操作。
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
5.枚举
- 线程安全
- 不是Lazy初始化
- 描述:它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。
public enum Singleton {
INSTANCE;
public void whateverMethod() {}
}
三、单例模式实现方式对比
方式 | 优点 | 缺点 |
懒汉模式 | 线程安全、懒加载 | 非懒加载 |
饿汉模式 | 线程安全、效率高 | 效率低 |
双重检测(DCL) | 线程安全、懒加载、效率高 | 实现难度较高 |
静态内部类 | 线程安全、懒加载、效率高 | 实现难度一般 |
枚举 | 线程安全、效率高 | 非懒加载 |
四、使用情况(经验之说)
在面试的时候,建议使用双检锁的方式,进而引出synchronized锁!!!其他情况下:不建议使用懒汉方式,可以使用饿汉方式。明确懒加载效果时,建议使用静态内部类。如果涉及到反序列化创建对象时,可以使用枚举的方式。其它需求的情况下,可以使用双检锁方式。