面试中的单例问题

简介: 面试中的单例问题

当我兴冲冲的带着笔记答案参加面试时,突然发现面前的面试官显得很严肃而且眉头紧锁,不知道是工作太累了,还是说他对今天的面试官不是很满意。


于是我就勇敢的坐过去在他的面前坐了下来,没想到第一道题就让面试官看出了我的水平,因此今天跟大家聊聊面试中单例的问题,希望大家都能了解这块内容。


在早期的项目代码中,如果我们想使用类的某个方法,我们基本都会创建一个类的对象实例然后再调用方法,这样的实现往往在系统内就会存在某个类的大量实例。如此一来,项目框架很难管理大量的对象,而且如果java虚拟机不能及时回收,容易造成内存溢出。


首先我们要明白什么是单例,所谓单例就是说在项目框架内某个类的对象实例只存在一个,任何调用方获取到的对象实例都是一个,那么很明显这个类是不能够被外部直接调用类构造器创建的。


我们先看下一个简单的单例设计:


image.png


上面代码在单线程是没有问题的,而且只有当线程调用类的静态方法时,才会生成类的静态变量。但是当多线程访问时,上面代码是有问题的,会生成多个对象的实例。


那么,我们可以用另外一种方法实现,比如说在类加载时候就初始化对象的实例,这样后面无论怎么调用类静态方法都不创建新的实例。还有一种方法,但是会牺牲部分系统性能,意思就是在多线程访问方法时通过锁机制让线程排队访问。我们先通过在类方法上加锁来实现类的单例,比如:


image.png


上述方法能实现单例,而且采用的思路是延迟加载,但是执行效率比较低。


之前有看到部分同学使用双重锁(Double CheckLock)机制来实现单例模式,一方面需要在实例上加上volatile关键字通知操作系统实现线程访问时内存屏障,然后还需要在方法中通过虚拟机实现的synchronized来同步方法访问,写法如下:


image.png


反正,我认为上面的实现是比较复杂的,大家需要去了解的知识点比较多,比如volatile ,synchronized,内存屏障。因此我不建议大家用这种方式,可以作为技术了解下还是有好处的,毕竟如果能跟面试官探讨到这一步,还是会加一些分的。


如果说我们不考虑服务负载问题,在多线程环境下可以预先加载类的静态实例,当虚拟机加载完成类后就会创建类的静态变量,甭管你到时用不用,反正给你留在那里。所有线程访问到的都是同一静态实例,有人也称这种方式为饿汉式,具体写法如下:


image.png


上面写法实现单例也是没有问题的,但是有些同学就会觉得如果我只是想调用一个类的某个静态方法,并不想生成它的实例,那有没有其他方法呢,经过各路大神的指点结合自身的总结,可以使用内部静态类来实现这个需求。


开发的同学都知道,虚拟机在加载类的过程中一开始并不会初始化类的内部静态类。如果线程调用内部静态类时,虚拟机只会初始化一次,这样既可以实现单例,同时也是线程安全的。具体写法如下:


image.png


除了以上讲到的几种方式外,JDK自身的枚举类型本身就是单例的实现,调用者不能显式的调用构造器完成实例创建,因此很多Java规范文档推荐使用枚举来实现单例。


当然对于初级开发人员而言,现在的主流开发框架都提供单例/多例模式供开发者选择,这样的好处让开发者更多关注业务功能开发,而不用过多关注虚拟机内部类实例创建问题。例如spring中默认类注入就是单例的,可以根据实际情况设置scope为singleton(单例)或者prototype(多例),如下图所示:


image.png


由于本人技术水平还有很大的上升空间,文中存在讲述不清楚或者错误的地方,请大家批评指正。


相关文章
|
6月前
|
消息中间件 安全 Java
面试官:单例Bean一定不安全吗?实际工作中如何处理此问题?
面试官:单例Bean一定不安全吗?实际工作中如何处理此问题?
103 5
|
设计模式 Java
Java反射(Class、反射实例化、反射与单例、获取类结构)附带相关面试题
1.了解反射,2.Class类的三种实例化方法,3.反射机制与对象实例化,4.反射与单例设计模式,5.通过反射获取类结构的信息
305 0
|
4月前
|
设计模式 存储 安全
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
62 1
|
4月前
|
设计模式 安全 NoSQL
Java面试题:结合单例模式与Java内存管理,设计一个线程安全的单例类?分析Java多线程工具类ExecutorService与Java并发工具包中的工具类,设计一个Java并发框架的分布式锁实现
Java面试题:结合单例模式与Java内存管理,设计一个线程安全的单例类?分析Java多线程工具类ExecutorService与Java并发工具包中的工具类,设计一个Java并发框架的分布式锁实现
64 0
|
4月前
|
设计模式 存储 缓存
Java面试题:结合单例模式与Java内存模型,设计一个线程安全的单例类?使用内存屏障与Java并发工具类,实现一个高效的并发缓存系统?结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
Java面试题:结合单例模式与Java内存模型,设计一个线程安全的单例类?使用内存屏障与Java并发工具类,实现一个高效的并发缓存系统?结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
39 0
|
设计模式 Java 安全
Java中包的作用以及使用、访问权限、单例/多例设计模式、枚举和异常(附带相关面试题)
本章节讲述以下方面知识点 1.包的作用以及使用 2.单例/多例化设计模式 3.枚举 4.访问权限2.异常
80 0
Java中包的作用以及使用、访问权限、单例/多例设计模式、枚举和异常(附带相关面试题)
|
SQL 安全 Java
Java高频面试题:在DCL单例写法中,为什么主要做两次检查?
有位工作5年的小伙伴,面试的时候被问到这样一道题,说在DCL单例写法中,为什么要做两次检查。要回答好这个问题,需要知道DCL单例的写法以及为什么要这样写? 今天,我给大家详细分析一下。
138 0
|
设计模式 安全 Java
第一季:2单例设计模式【Java面试题】
第一季:2单例设计模式【Java面试题】
74 0
|
设计模式 安全 Java
常见面试题!6种单例设计模式的实现
枚举类天然防止反序列化。 其他单例模式 可以通过 重写 readResolve() 方法,从而防止反序列化,使实例唯一重写 readResolve() :
119 0
常见面试题!6种单例设计模式的实现
|
应用服务中间件
秋招面试:如何证明Servlet是单例的?
秋招面试:如何证明Servlet是单例的?
下一篇
无影云桌面