ArrayBlockingQueue vs LinkedBlockingQueue,99%的人答不全!
引言
小米最近在刷社招面試題,發(fā)現(xiàn)“ArrayBlockingQueue 和 LinkedBlockingQueue 的區(qū)別”頻繁出現(xiàn)在面試官的題庫(kù)里。
作為一名喜歡分享技術(shù)的 31 歲“老程序員”,小米覺(jué)得有必要深挖一波,順便給大家講講這兩位 Java 并發(fā)容器中的明星選手。畢竟,光會(huì) API 可不夠,面試官喜歡追著問(wèn)底層原理!
好了,故事開(kāi)始了!
起源:為什么需要阻塞隊(duì)列?
某一天,小米在公司加班,產(chǎn)品經(jīng)理老王突然跑來(lái):“小米,我們的任務(wù)隊(duì)列經(jīng)常出錯(cuò),你看看咋回事?”
小米打開(kāi)日志一看,原來(lái)是有些線(xiàn)程在消費(fèi)任務(wù)的時(shí)候隊(duì)列為空,導(dǎo)致 NullPointerException,而生產(chǎn)者又可能突然暴增,導(dǎo)致 OutOfMemoryError。
于是,小米一拍腦袋:“這就是典型的生產(chǎn)者-消費(fèi)者問(wèn)題,用阻塞隊(duì)列就能解決!”
阻塞隊(duì)列的作用:
- 生產(chǎn)者-消費(fèi)者模型:生產(chǎn)者不斷往隊(duì)列里放數(shù)據(jù),消費(fèi)者不斷從隊(duì)列取數(shù)據(jù),生產(chǎn)快了隊(duì)列會(huì)阻塞生產(chǎn)者,消費(fèi)慢了隊(duì)列會(huì)阻塞消費(fèi)者,保證了流量的平穩(wěn)。
- 線(xiàn)程間通信:不需要使用 synchronized 和 wait/notify,內(nèi)部已經(jīng)封裝好了鎖機(jī)制,簡(jiǎn)化多線(xiàn)程編程。
- 防止過(guò)載:隊(duì)列可以設(shè)置容量上限,防止生產(chǎn)過(guò)快導(dǎo)致 OOM。
java.util.concurrent 包里提供了多個(gè)阻塞隊(duì)列,今天我們重點(diǎn)講解ArrayBlockingQueue 和 LinkedBlockingQueue,它們是面試中最常考的兩位“老大哥”。
ArrayBlockingQueue:數(shù)組實(shí)現(xiàn)的有界隊(duì)列
基本特點(diǎn):
- 基于數(shù)組,底層是 Object[],容量固定(有界隊(duì)列)。
- FIFO(先進(jìn)先出),頭部取數(shù)據(jù),尾部插入數(shù)據(jù)。
- 數(shù)據(jù)結(jié)構(gòu)簡(jiǎn)單,內(nèi)存占用低,適合高性能場(chǎng)景。
- 單一 ReentrantLock 進(jìn)行并發(fā)控制,鎖粒度大,生產(chǎn)和消費(fèi)不能同時(shí)進(jìn)行。
源碼解析:
圖片
ArrayBlockingQueue 面試核心點(diǎn)
- 容量固定,創(chuàng)建時(shí)必須指定大小,不能動(dòng)態(tài)擴(kuò)容。
- 性能較高,但由于采用單一鎖,并發(fā)能力稍差。
- 數(shù)據(jù)存放在數(shù)組中,適合元素?cái)?shù)量穩(wěn)定的場(chǎng)景,避免頻繁擴(kuò)容帶來(lái)的開(kāi)銷(xiāo)。
LinkedBlockingQueue:鏈表實(shí)現(xiàn)的有界/無(wú)界隊(duì)列
基本特點(diǎn):
- 基于鏈表,底層是 Node<E> 鏈表,可以是有界隊(duì)列(指定大小)或無(wú)界隊(duì)列(默認(rèn) Integer.MAX_VALUE)。
- FIFO(先進(jìn)先出),頭部取數(shù)據(jù),尾部插入數(shù)據(jù)。
- 雙鎖機(jī)制,生產(chǎn)和消費(fèi)使用不同的 ReentrantLock,并發(fā)能力更強(qiáng)。
- 適合吞吐量要求較高的場(chǎng)景,但鏈表結(jié)構(gòu)可能增加 GC 壓力。
源碼解析:
圖片
LinkedBlockingQueue 面試核心點(diǎn)
- 默認(rèn)無(wú)界,但可以指定大小,防止 OOM。
- 性能更高,采用雙鎖機(jī)制,生產(chǎn)者和消費(fèi)者可以并行執(zhí)行。
- 數(shù)據(jù)存放在鏈表中,適合元素?cái)?shù)量不確定的場(chǎng)景,但鏈表結(jié)構(gòu)可能增加內(nèi)存開(kāi)銷(xiāo)和 GC 壓力。
場(chǎng)景對(duì)比:哪種更適合你的項(xiàng)目?
圖片
總結(jié)選擇指南:
- 數(shù)據(jù)量穩(wěn)定,低延遲需求,選 ArrayBlockingQueue(如高頻交易系統(tǒng))。
- 數(shù)據(jù)量不確定,吞吐量高,選 LinkedBlockingQueue(如日志收集、任務(wù)隊(duì)列)。
- 避免無(wú)界 LinkedBlockingQueue,否則可能導(dǎo)致 OOM。
面試真題實(shí)戰(zhàn)
面試題 1:你會(huì)怎么選擇 ArrayBlockingQueue 和 LinkedBlockingQueue?
答:
- 數(shù)據(jù)量穩(wěn)定,需要高性能:ArrayBlockingQueue
- 數(shù)據(jù)量不確定,需要高吞吐:LinkedBlockingQueue
- 防止 OOM,使用 LinkedBlockingQueue 時(shí)務(wù)必指定容量
面試題 2:為什么 LinkedBlockingQueue 并發(fā)能力比 ArrayBlockingQueue 強(qiáng)?
答:
- LinkedBlockingQueue 采用雙鎖機(jī)制(生產(chǎn)者和消費(fèi)者各有一把鎖),生產(chǎn)和消費(fèi)可并行。
- ArrayBlockingQueue 采用單鎖機(jī)制,生產(chǎn)和消費(fèi)不能并行,導(dǎo)致并發(fā)能力稍遜。
總結(jié)
今天,我們通過(guò)小米的面試經(jīng)歷,深入解析了 ArrayBlockingQueue 和 LinkedBlockingQueue。面試官愛(ài)問(wèn)的點(diǎn),我們都幫大家整理好了!
核心記住三點(diǎn):
- 固定容量 vs 可擴(kuò)展性:ArrayBlockingQueue 容量固定,LinkedBlockingQueue 可以無(wú)界。
- 單鎖 vs 雙鎖:LinkedBlockingQueue 并發(fā)能力更強(qiáng)。
- 適用場(chǎng)景不同:ArrayBlockingQueue 適合高性能場(chǎng)景,LinkedBlockingQueue 適合高吞吐量場(chǎng)景。