什么是单例模式:顾名思义就是只有一个实例,并且它自己负责创建自己的对象,用于确保一个类只有一个实例,并提供一个全局访问点,且可以直接访问,不需要实例化该类的对象。
核心代码:构造方法私有化,private。
1、懒汉式
懒汉式,顾名思义就是实例在用到的时候才去创建,“比较懒”,用的时候才去检查有没有实例,如果有则返回,没有则新建。有线程安全和线程不安全两种写法,区别就是synchronized关键字。
线程不安全
- 这种方式在第一次调用 getInstance() 方法时才创建实例,属于懒加载的方式。
- 优点是延迟加载,节省了内存空间。
- 缺点是在多线程环境下可能会创建多个实例,不是线程安全的。
public class Singleton {
private static Singleton instance;
private Singleton() {} //构造方法私有
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
线程安全,同步方法
- 在懒汉式的基础上,通过在 getInstance() 方法上加上 synchronized 关键字,实现线程安全。
- 优点是在多线程环境下避免了创建多个实例,保证了线程安全性。
- 缺点是每次获取实例都需要进行同步,造成了一定的性能损耗。
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
2、饿汉式
饿汉式,从名字上也很好理解,就是“比较勤”,实例在初始化的时候就已经建好了,不管你有没有用到,都先建好了再说。好处是没有线程安全的问题,坏处是浪费内存空间。
静态代码块
public class Singleton {
private static final Singleton instance;
static {
instance = new Singleton();
}
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
静态常量
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
3、双重检查锁(Double-Checked Locking)
- 这种方式在懒汉式的基础上进行了优化,看上面代码实现中,特点是在synchronized关键字内外都加了一层 if 条件判断,这样既保证了线程安全,又比直接上锁提高了执行效率,还节省了内存空间。
- 通过使用 volatile 关键字保证了多线程环境下的可见性。
- 优点是延迟加载、线程安全且高效率。
- 缺点是实现稍微复杂,并且在早期的 Java 版本中会存在一些问题,需要注意线程安全性。
public class Singleton {
private volatile static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
4、静态内部类
- 通过静态内部类的方式可以实现延迟加载并确保线程安全。
- 这种方式利用了类加载机制和静态内部类的特性来实现懒加载和线程安全。
- 优点是既实现了延迟加载又保证了线程安全,并且代码相对简洁。
- 缺点是需要理解类加载过程和静态内部类的相关知识。
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
5、枚举类
- 枚举类型是线程安全的,并且只会装载一次,因此可以作为单例模式的实现方式。
- 优点是简单明了,由 JVM 保证线程安全和单例特性。
- 缺点是枚举类在某些情况下可能不太灵活,不支持懒加载。
public enum Singleton {
INSTANCE;
public void doSomething() {
// 实现单例的方法
}
}
总结下:
- 懒汉式:在一般情况下,包括线程安全和线程不安全的懒汉式方式使用较少,因为存在线程安全和性能等方面的考虑。
- 饿汉式:饿汉式和双检锁方式可以根据具体情况选择使用,适合于在程序初始化时就创建实例的场景。
- 静态内部类:静态内部类的实现方式可以实现延迟加载并确保线程安全,适合需要明确实现 lazy loading 效果的情况。
- 枚举方式:枚举方式也是一种简洁且线程安全的单例模式实现方式,适合涉及到反序列化创建对象的情况。