你好,我是风一样的树懒,一个工作十多年的后端开发,曾就职京东、阿里等多家互联网头部企业。
文章可能会比较长,主要解析的非常详解,或涉及一些底层知识,供面试高阶难度用。可以根据自己实际理解情况合理取舍阅读
手写一个单例模式是面试中常见的题目之一。下面是单例模式的不同实现方式,包括 懒汉式、饿汉式、双重检查锁(DCL) 和 枚举类型 的实现。每种方式的实现有其适用场景和优缺点。
饿汉式是最简单的单例实现,类在加载时就创建实例,线程安全,不会出现多线程问题,但如果实例非常重,且并不一定会使用到,可能会浪费资源。
public class Singleton {
// 创建一个静态常量来保存实例
private static final Singleton instance = new Singleton();
// 私有化构造方法,防止外部直接创建实例
private Singleton() {}
// 提供一个公共的静态方法来访问单例
public static Singleton getInstance() {
return instance;
}
}
懒汉式单例在第一次使用时才会创建实例。为了确保线程安全,通常会使用 synchronized 或 volatile。
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
这种方式存在线程安全问题,多个线程可能会在 instance 为 null 时同时创建多个实例。
通过 synchronized 修饰 getInstance() 方法来确保线程安全,但每次调用时都会加锁,性能较差。
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
双重检查锁定(Double-Checked Locking)是懒汉式的优化版本,先检查实例是否为 null,若为 null,才加锁创建实例。在加锁后,再检查一次实例是否已经创建,避免重复加锁。
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
volatile 保证实例的可见性,防止指令重排,确保在多线程下的正确性。
双重检查减少了不必要的同步锁,提高了性能。
静态内部类单例是实现单例模式的推荐方式。它结合了懒加载和线程安全的优点,并且通过 JVM 的类加载机制来确保实例的唯一性。
public class Singleton {
// 静态内部类
private static class SingletonHelper {
// 静态初始化器,在类加载时创建实例
private static final Singleton instance = new Singleton();
}
private Singleton() {}
public static Singleton getInstance() {
return SingletonHelper.instance;
}
}
为什么线程安全:静态内部类在被加载时,JVM 会确保它的线程安全,只有在第一次调用 getInstance() 时,SingletonHelper 才会被加载,因此线程安全且延迟加载。
优点:避免了双重检查锁定的复杂性,且具备懒加载的特点。
在 Java 中,枚举类型是创建单例的最推荐方式,JVM 会自动保证枚举实例的唯一性和线程安全。
public enum Singleton {
INSTANCE;
// 其他方法
public void someMethod() {
System.out.println("Singleton method");
}
}
为什么线程安全:JVM 在加载枚举时会确保实例的唯一性,并且它是线程安全的。避免了其他实现方式可能出现的问题(如反射或序列化攻击)。
优点:实现简单,避免了反射、序列化等攻击方式。
饿汉式:简单易懂,线程安全,缺点是即使不需要该实例,JVM 也会在启动时创建。
懒汉式:延迟加载,但需要注意线程安全问题,synchronized 会影响性能。
双重检查锁:解决了懒汉式性能问题,但实现较为复杂。
静态内部类:是目前最推荐的单例实现方式,线程安全且懒加载。
枚举:最安全且最简洁的单例实现,JVM 会确保枚举实例的唯一性和线程安全。
如果面试中让你手写,你会写哪一个?
我可能会从第二个里面挑一个写,因为这样,面试官可能会继续问题,然后再引申到写第三个。
今天的内容就分享到这儿,喜欢的朋友可以关注,点赞。有什么不足的地方欢迎留言指出,您的关注是我前进的动力!