面试题答案
一键面试懒加载(Lazy Loading)
- 内存资源:
- 优点:在类加载时,不会立即创建单例实例,只有在第一次调用获取实例的方法时才进行实例化。这意味着如果在整个应用生命周期中,该单例对象从未被使用,那么就不会占用额外的内存空间。
- 缺点:由于实现懒加载通常需要额外的同步机制(如双重检查锁定)来确保多线程环境下的线程安全,这会引入一些额外的代码和对象,可能会占用少量额外的内存。例如,在双重检查锁定中使用的
volatile
关键字,会增加一定的内存开销用于保证可见性和禁止指令重排。
- 性能:
- 优点:首次调用获取实例方法时才创建实例,对于一些资源消耗大且可能不会用到的单例对象,能有效延迟资源分配,提高应用启动速度。
- 缺点:多线程环境下实现懒加载的同步机制会带来性能开销。例如,双重检查锁定中,第一次检查实例是否为
null
时,若为null
,则需要进入同步块进行实例化。同步块的竞争会导致线程等待,降低并发性能。同时,volatile
关键字带来的可见性和禁止指令重排的保障也会有一定的性能损耗。
饿汉式加载(Eager Loading)
- 内存资源:
- 优点:实现简单,没有复杂的同步机制带来的额外内存开销。
- 缺点:类加载时就创建单例实例,无论该实例是否会被使用,都会占用内存空间。如果单例对象占用内存较大,且应用中很多单例对象采用饿汉式加载,可能会导致应用启动时内存占用过高,甚至引发内存不足问题。
- 性能:
- 优点:由于在类加载时就完成实例化,不存在多线程同步问题,获取实例的方法没有同步开销,在多线程环境下性能较好。获取实例时直接返回已创建好的实例,速度快。
- 缺点:类加载时就创建实例,如果单例对象初始化过程复杂,会导致类加载时间变长,从而影响应用的启动性能。
应用场景选择
- 懒加载适用场景:
- 资源敏感场景:当单例对象占用大量资源,且不确定在应用运行过程中是否会使用到该单例对象时,懒加载能有效避免不必要的内存浪费。例如,一些后台管理模块中的数据统计单例服务,只有在用户主动查看统计报表时才需要使用,采用懒加载可在应用启动时不占用该部分内存。
- 多线程并发度不高场景:如果应用中多线程并发访问获取单例实例的频率较低,懒加载带来的同步开销对性能影响不大,此时可以选择懒加载。
- 饿汉式加载适用场景:
- 对启动性能要求不高但对运行时性能要求高的场景:如数据库连接池单例,应用启动时创建连接池实例虽然会使启动时间稍有增加,但在运行过程中频繁获取数据库连接时,由于不存在同步开销,能快速返回连接,提高运行时性能。
- 多线程并发度高场景:在高并发环境下,饿汉式加载没有同步问题,能保证高效的并发访问,避免了懒加载同步机制带来的性能瓶颈。例如,在高并发的 Web 应用中,一些全局配置信息的单例对象,采用饿汉式加载可确保多个线程快速获取配置信息。