单例模式是一种常用的软件设计模式,其目的是确保一个类仅有一个实例,并提供一个全局访问点来获取这个唯一实例。在Java中,实现单例模式通常需要遵循以下几个关键原则:
私有化构造器:将类的构造器声明为private
,以防止外部代码通过new
操作符直接创建该类的实例。
静态工厂方法:提供一个静态方法(通常称为getInstance()
),用于获取单例对象。这个方法负责检查是否已经创建过实例,如果尚未创建,则创建并保存;如果已存在,则直接返回该实例。
确保线程安全:在多线程环境中,必须确保单例对象的创建过程是线程安全的,即无论何时何地,无论多少个线程同时请求,都只会创建一个实例。
以下是几种常见的Java单例模式实现方式及其示例:
优点:类加载时即初始化单例,线程安全,无同步开销。
缺点:如果单例实例很庞大或创建过程耗时,可能会导致类加载时较长的初始化时间,且即使从未使用该单例,也会占用内存。
public class SingletonEager {
private static final SingletonEager INSTANCE = new SingletonEager();
private SingletonEager() {}
public static SingletonEager getInstance() {
return INSTANCE;
}
}
优点:延迟初始化,只有在首次调用getInstance()
时才创建单例。
缺点:线程不安全,多线程环境下可能创建多个实例。
public class SingletonLazyUnsafe {
private static SingletonLazyUnsafe instance;
private SingletonLazyUnsafe() {}
public static SingletonLazyUnsafe getInstance() {
if (instance == null) {
instance = new SingletonLazyUnsafe();
}
return instance;
}
}
优点:解决了线程安全问题。
缺点:每次调用getInstance()
都会进行同步,造成不必要的性能损耗。
public class SingletonLazySynchronizedMethod {
private static SingletonLazySynchronizedMethod instance;
private SingletonLazySynchronizedMethod() {}
public static synchronized SingletonLazySynchronizedMethod getInstance() {
if (instance == null) {
instance = new SingletonLazySynchronizedMethod();
}
return instance;
}
}
优点:既实现了延迟初始化,又保证了线程安全,且仅在初始化时加锁,性能较高。
缺点:依赖JVM正确实现内存模型,早期JVM版本可能存在DCL失效问题,但在现代JVM中已得到解决。
public class SingletonDCL {
private volatile static SingletonDCL instance;
private SingletonDCL() {}
public static SingletonDCL getInstance() {
if (instance == null) {
synchronized (SingletonDCL.class) {
if (instance == null) {
instance = new SingletonDCL();
}
}
}
return instance;
}
}
优点:利用类加载机制保证线程安全,延迟初始化,无同步开销。
缺点:相比其他实现方式,代码结构稍显复杂。
public class SingletonStaticInnerClass {
private SingletonStaticInnerClass() {}
private static class SingletonHolder {
private static final SingletonStaticInnerClass INSTANCE = new SingletonStaticInnerClass();
}
public static SingletonStaticInnerClass getInstance() {
return SingletonHolder.INSTANCE;
}
}
Spring框架广泛使用了单例模式,尤其是在其核心组件BeanFactory
中管理的bean实例。Spring默认将配置文件中定义的bean配置为单例模式,这意味着对于每个指定为单例的bean,Spring容器在整个应用程序生命周期中仅创建一次该bean的实例,并将其缓存起来,后续对该bean的所有请求都将返回相同的实例。
作用:
由于Spring源码庞大且涉及众多模块,这里无法直接给出全部相关源码。但可以简述Spring如何实现单例模式:
Spring的DefaultListableBeanFactory
类(或其他实现BeanFactory
接口的类)是Spring IoC容器的核心实现,它维护了一个DefaultSingletonBeanRegistry
(或类似实现SingletonBeanRegistry
接口的类)来管理单例bean。当容器创建一个bean时,首先检查是否已经存在该bean的单例实例。如果存在,直接从缓存中返回;否则,按照bean定义创建新实例,并将其放入缓存中。
以下是一段简化版的伪代码,以说明Spring如何通过SingletonBeanRegistry
来实现单例模式:
public interface BeanFactory {
Object getBean(String beanName);
}
public interface SingletonBeanRegistry {
Object getSingleton(String beanName);
void registerSingleton(String beanName, Object singletonObject);
}
public class DefaultListableBeanFactory implements BeanFactory {
private final SingletonBeanRegistry singletonRegistry;
public Object getBean(String beanName) {
return this.singletonRegistry.getSingleton(beanName);
}
}
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
public Object getSingleton(String beanName) {
return this.singletonObjects.get(beanName);
}
public void registerSingleton(String beanName, Object singletonObject) {
this.singletonObjects.putIfAbsent(beanName, singletonObject);
}
}
实际Spring源码中,DefaultSingletonBeanRegistry
及其相关类还包含了许多额外逻辑,如处理循环依赖、bean的后置处理器、生命周期回调等。以上伪代码仅为了说明Spring如何通过一个注册表(registry)来管理单例bean的创建和缓存,确保整个应用中每个bean名称对应唯一实例。