“小米,你面试那家十八线大厂的 JVM 题都问了啥?”

“栈和队列!你猜我怎么答的?我直接讲了个日常故事,面试官听完都笑了——然后就让我进二面了!”

社招面试现场,小米又被问到“老问题”

事情发生在不久前的某次 JVM 社招面试中。作为一个写 Java 写了快十年的开发者,我对 JVM 这块多少还是有点底气的。可没想到,刚坐下,面试官的第一个问题却不是问 GC,也不是类加载,而是:

“你能说说什么是栈和队列吗?它们在 JVM 里有什么应用,区别又在哪里?”

你是不是也觉得这题太基础了?但我告诉你——越基础的题目,越能看出候选人的基本功和表达能力。

我当时没有急着丢概念,而是笑着说:

“那我就给您讲个故事吧,栈和队列的故事,就像生活中我们排队买奶茶和叠衣服的过程一样。”

从生活场景讲起:奶茶店和衣柜

故事一:排队买奶茶——这是“队列”Queue

想象一下你中午和同事去奶茶店,人太多了,你只能排队。

每来一个人,都排在你后面;而奶茶店做完一个奶茶,是先给最前面那位顾客,对吧?这就是典型的先进先出(FIFO)的规则:

  • 谁先来,谁先拿;
  • 后来的人只能等前面的人拿完;
  • 加塞是禁止的(当然现实可能会有,但数据结构中不允许)。

所以,我们可以说:“排队买奶茶”,就是队列 Queue的生活版写照!

故事二:叠衣服进衣柜——这是“栈”Stack

再来看另一个场景:你周末打扫卫生,把衣服一件件折好往柜子里叠。

每一件新衣服,都放在上一件的上面;而你下次想拿衣服,肯定是从最上面拿,对吧?也就是最后放进去的,最先拿出来。

这就是后进先出(LIFO)的规则。

  • 谁最后放进去,谁最先拿出来;
  • 如果你想拿最下面的那件衣服,是不是还得先拿出上面所有的衣服?这很麻烦,但这就是的特性。

所以我们说,叠衣服进柜子,就是栈 Stack的生活版写照!

从生活回到技术:栈和队列的定义与区别

故事讲完了,面试官已经笑出了声。我乘胜追击,马上补充了技术定义:

栈(Stack)

  • 定义:一种受限的线性表,只允许在一端进行插入和删除操作。
  • 操作方向:只能在“栈顶”操作。
  • 顺序特性:后进先出(LIFO)。
  • 常用操作
  • push():压栈
  • pop():出栈
  • peek():查看栈顶元素但不移除

队列(Queue)

  • 定义:一种受限的线性表,只允许在一端插入、另一端删除
  • 操作方向:插入在“队尾”,删除在“队头”。
  • 顺序特性:先进先出(FIFO)。
  • 常用操作
  • offer() / add():入队
  • poll() / remove():出队
  • peek() / element():查看队头元素

栈 vs 队列 核心区别

JVM社招面试题:队列和栈是什么?有什么区别?我在面试现场讲了个故事…_JVM

JVM 中的栈和队列:不仅仅是数据结构

聊完概念,我补了一句:“其实 JVM 里,‘栈’和‘队列’都无处不在!”

面试官点点头,于是我开始举例:

1. 栈在 JVM 中的应用:Java 虚拟机栈

JVM 中的每个线程都会分配一个 虚拟机栈(Java Virtual Machine Stack),这个栈里保存的是:

  • 栈帧(Stack Frame):代表一个方法的调用;
  • 每个栈帧包括局部变量表、操作数栈、方法返回地址等。

每次方法调用,就会压入栈帧;方法执行结束,就会弹出栈帧

是不是就像我们上面说的“叠衣服”?调用链越深,衣服堆得越高;异常时如果不处理,直接堆栈溢出,就是“StackOverflowError”。

2. 队列在 JVM 中的应用:线程池任务调度

再说说队列的使用,最典型的就是线程池:

线程池中的任务,不可能同时执行,怎么办?就用阻塞队列(如 LinkedBlockingQueue)来排队!

比如你提交了 10 个任务,但只有 5 个线程,前 5 个任务会立即执行,后 5 个会排在队列中等待调度。

队列的设计让线程池调度更高效,也避免了线程频繁创建销毁。

面试官继续追问:“那你用过哪些实现类?”

这个问题就涉及 Java 里提供的数据结构了,我也提前准备过,直接开讲:

栈的实现类:

  • Stack<E>:古老的类,继承 Vector,线程安全,但已不推荐;
  • 推荐使用:Deque 接口的 ArrayDeque 作为栈的替代方案,效率更高。

JVM社招面试题:队列和栈是什么?有什么区别?我在面试现场讲了个故事…_栈和队列_02

队列的实现类:

  • LinkedList<E>:实现了 Queue 接口,可作为普通队列;
  • PriorityQueue<E>:优先队列,元素会按优先级排列;
  • ArrayDeque<E>:双端队列,高效无锁;
  • ConcurrentLinkedQueue<E>:并发无锁队列,适合多线程场景;
  • BlockingQueue<E>(接口):
  • 实现类如 LinkedBlockingQueue、ArrayBlockingQueue、PriorityBlockingQueue 等;
  • 支持线程阻塞等待入队或出队,广泛用于线程池。

面试官最后一问:如果你来设计一个栈,怎么实现?

我一笑,说:“我就拿数组模拟一个栈,每次 push 就往后放,pop 就返回最后一个元素,当然还得处理溢出和动态扩容。”

并简单描述了代码逻辑,用 ArrayList 或 LinkedList 都可以轻松实现。

面试官点头:“你讲得很有条理,故事化地解释也挺好,二面通知等 HR 吧。”

我心里暗自窃喜:讲技术,不一定非得干巴巴。讲故事、类比生活,有时更打动人。

总结:栈和队列,基础但不简单

写到这里,咱们来回顾一下:

  • 栈:后进先出(叠衣服)
  • 队列:先进先出(买奶茶)
  • JVM 中栈用于方法调用,队列用于线程调度;
  • 面试不仅考你会不会,还考你“讲不讲得清”!

最后的小米叮咛

“基础不牢,地动山摇。”

栈和队列是最基础的数据结构,但千万别轻视它。你能把它讲清楚、讲有趣,才能让面试官眼前一亮。

你也可以试试讲讲:

  • 浏览器的“后退”按钮是怎么用栈实现的?
  • Kafka、消息队列中的“消费逻辑”是不是也是队列?
  • Spring 的责任链模式用的是哪种结构?

END

欢迎评论区留言,一起交流!你还遇到过哪些看似简单却被问懵的面试题?

我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!