一、单例模式介绍
单例模式(Singleton Pattern)是一种常用的软件设计模式,它确保一个类仅有一个实例,并提供一个全局访问点来获取该实例。这个模式在多种场景下都非常有用,比如配置文件的读取、数据库连接、线程池等,这些资源通常只需要一个实例来管理,以避免资源浪费或数据不一致。
单例模式的面试题很多,例如:
手写一个单例模式?--主要考察对单例的理解及线程安全环境的理解。
spring是如何实现单例模式的?--主要考察对spring源代码级别的类创建方式理解。
1.单例模式的实现方式
单例模式有多种实现方式,但主要目的是确保类的一个全局访问点和一个实例。以下是几种常见的实现方式(感觉是孔乙己对茴香豆的N种写法):
懒汉式(线程不安全)
懒汉式单例模式在第一次被引用时实例化,但这种方式在多线程环境下可能会创建多个实例。
public class Singleton {
private static Singleton instance; //instance必须为static,变量属于类全局
private Singleton() {} //构造函数必须为private,否则谁都可以new了。
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;
}
}
双重检查锁定(Double-Checked Locking)
双重检查锁定模式既保证了线程安全,又避免了不必要的同步开销。这种方式是懒汉式最佳方案了。
为什么要用volatile看这个文章:【面试题】为什么单例一定要加 volatile?【懒汉模式为什么要用volatile】【volatile的作用】_为啥单例要使用volatile-CSDN博客
public class Singleton {
//volatile是为了保证实例可见性,避免指令重排序,从而避免半初始化问题
private static volatile Singleton instance;
private Singleton() {} //构造函数必须私有
public static Singleton getInstance() {
if (instance == null) { //这一层检查是为了提升性能,如果有对象,就直接返回了
synchronized (Singleton.class) { //锁加在类对象上,全局唯一
//new单例之前,先判断,这步很关键,如果删除这个判断,可能会生成多个实例。
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
饿汉式
饿汉式单例模式在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快。
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
静态内部类
静态内部类方式利用了类加载机制来保证初始化实例时只有一个线程,并且实现了懒加载。
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
枚举
枚举方式是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
二、注意事项
- 单例模式虽然简单,但使用时也需要注意线程安全和性能问题。
- 在某些场景下,可能需要考虑单例的销毁和资源的释放,尤其是在长时间运行的应用中。
- 枚举方式是实现单例模式的推荐方式,因为它既简单又安全。但是这个用起来比较别扭,我还是喜欢饿汉式,毕竟自己实现的单例不多,这点启动时间还是可以接受的。
如果觉得对你有用,记得点赞 关注。