面试必问题:单例模式的5种写法

单例模式主要有以下几种分类:

  • 饿汉式单例:在类加载时就创建单例对象,优点是实现简单,线程安全,缺点是如果单例对象在程序运行过程中一直未被使用,会造成资源浪费。例如:
public class Singleton {
    private static Singleton instance = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return instance;
    }
}
  • 懒汉式单例:在第一次调用 getInstance 方法时才创建单例对象,优点是可以延迟加载,节省资源,但是在多线程环境下需要考虑线程安全问题。例如:

public class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
  • 双重检查锁单例:在懒汉式单例的基础上,通过双重检查锁机制来提高性能并保证线程安全。例如:

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;
    }
}
  • 静态内部类单例:利用静态内部类的特性来实现单例,既保证了线程安全,又实现了延迟加载。例如:

public class Singleton {
    private Singleton() {}

    private static class SingletonHolder {
        private static final Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}
  • 枚举单例:通过枚举类型来实现单例,不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。例如:

public enum Singleton {
    INSTANCE;

    public void doSomething() {
        // 单例对象的方法
    }
}

在多线程环境下,保证单例模式的线程安全可以采用以下几种方法:

  • 使用 synchronized 关键字:在获取单例对象的方法上添加 synchronized 关键字,如懒汉式单例中的 getInstance 方法,这样可以保证在同一时刻只有一个线程能够访问该方法,从而避免多个线程同时创建单例对象。但是这种方式会降低性能,因为每次调用该方法都需要获取锁。
  • 双重检查锁机制:在懒汉式单例的基础上,通过双重检查锁机制来减少同步的开销。在第一次检查实例是否为 null 时,如果不为 null,则直接返回实例,不需要获取锁;只有在第一次检查为 null 时,才进入同步块再次检查并创建实例。同时,需要将实例变量声明为 volatile,以保证可见性和禁止指令重排序。
  • 静态内部类:利用静态内部类的特性,在类加载时不会立即初始化内部类,只有在第一次调用 getInstance 方法时,才会加载并初始化内部类,从而创建单例对象。由于类加载过程是线程安全的,所以这种方式可以保证单例的线程安全,并且实现了延迟加载。
  • 枚举:枚举类型在 Java 中是天然的单例,因为 Java 虚拟机在加载枚举类时会保证只有一个实例存在,并且枚举类的实例是线程安全的,所以可以通过枚举来实现单例模式,避免了复杂的线程安全处理。

关注公众号“大模型全栈程序员”回复“小程序”获取1000个小程序打包源码。更多免费资源在http://www.gitweixin.com/?p=2627

发表评论

邮箱地址不会被公开。 必填项已用*标注