
我發(fā)現(xiàn)這類標(biāo)題的問(wèn)題在于它把“八股文面試”描述得像背題庫(kù)就能過(guò)關(guān)的考試。實(shí)際上Java后端面試確實(shí)有大量需要記憶的知識(shí)點(diǎn)但面試官真正在意的不是你能不能背出某個(gè)結(jié)論而是你能不能講清楚結(jié)論背后的原理、適用條件和邊界?;?天速刷高頻題核心目標(biāo)不是“背完所有答案”而是建立一條自己的答題鏈路遇到一個(gè)技術(shù)問(wèn)題先判斷它屬于哪個(gè)知識(shí)域再按底層原理、實(shí)現(xiàn)方式、實(shí)際應(yīng)用、坑點(diǎn)和排查順序組織回答。這篇文章適合兩類人看一類是準(zhǔn)備校招或社招時(shí)間緊但基礎(chǔ)還不錯(cuò)需要系統(tǒng)過(guò)一遍高頻考點(diǎn)的人另一類是已經(jīng)工作一段時(shí)間想通過(guò)面試整理自己技術(shù)體系的開發(fā)。下面按我的實(shí)際復(fù)習(xí)經(jīng)驗(yàn)拆一遍“速刷八股文”到底該怎么刷。1. 先想清楚八股文后端面試到底在考什么很多人的誤區(qū)是一上來(lái)就找題單背完 Spring 背 MySQL背完 Redis 背消息隊(duì)列。結(jié)果遇到一個(gè)稍微追問(wèn)的場(chǎng)景題比如“緩存穿透、緩存擊穿、緩存雪崩分別怎么處理如果 Redis 掛了怎么辦”立刻就亂了。原因不是背得不夠而是沒有建立知識(shí)點(diǎn)之間的關(guān)聯(lián)。1.1 后端面試的高頻結(jié)構(gòu)其實(shí)是固定的Java 后端面試題看上去很多但按知識(shí)域拆開基本逃不出這幾個(gè)方向Java 基礎(chǔ)集合、String、異常、泛型、反射、序列化。并發(fā)編程synchronized、volatile、Lock、AQS、線程池、CAS、ThreadLocal。JVM內(nèi)存區(qū)域、垃圾回收、類加載、OOM 排查、JVM 調(diào)優(yōu)參數(shù)。Spring 家族IOC、AOP、Bean 生命周期、事務(wù)傳播機(jī)制、Spring Boot 自動(dòng)裝配。MySQL索引、事務(wù)隔離級(jí)別、MVCC、鎖、主從復(fù)制、慢查詢優(yōu)化。Redis數(shù)據(jù)結(jié)構(gòu)、過(guò)期策略、持久化、緩存三大問(wèn)題、分布式鎖。消息隊(duì)列選型對(duì)比、可靠性、順序性、重復(fù)消費(fèi)、消息積壓。分布式基礎(chǔ)CAP、BASE、分布式事務(wù)、接口冪等、負(fù)載均衡、服務(wù)治理。注意這些方向不是平等的。從面試頻率和復(fù)習(xí)性價(jià)比來(lái)看Java 基礎(chǔ)、并發(fā)、MySQL、Redis、Spring 這五塊最值得投入時(shí)間。JVM 在社招中經(jīng)常被深挖校招通常問(wèn)到內(nèi)存區(qū)域和垃圾回收算法就夠。消息隊(duì)列和分布式內(nèi)容很多但大多數(shù)崗位只要求你能講清楚一種主流組件的核心機(jī)制。1.2 速刷前先做一次能力自測(cè)我建議不要直接看題先花 30 分鐘做一次自測(cè)。拿一張白紙把上面幾個(gè)方向?qū)懴聛?lái)每個(gè)方向給自己回答三個(gè)問(wèn)題這個(gè)知識(shí)域里我能不看資料講清楚哪三個(gè)核心知識(shí)點(diǎn)哪些知識(shí)點(diǎn)我只能說(shuō)出名詞但解釋不了原理哪些知識(shí)點(diǎn)我連名詞都感覺很陌生自測(cè)的作用不是打擊信心而是幫你確定優(yōu)先級(jí)。3 天時(shí)間不可能把所有方向都刷到精通但可以做到高頻題不冷場(chǎng)。第一天優(yōu)先解決“完全陌生”和“半懂不懂”的高頻方向第二天把能講清楚的知識(shí)點(diǎn)整理成結(jié)構(gòu)化表達(dá)第三天用模擬問(wèn)答和項(xiàng)目串聯(lián)把所有內(nèi)容串起來(lái)。注意速刷的前提是你已經(jīng)寫過(guò) Java 項(xiàng)目哪怕是個(gè)簡(jiǎn)單的 CRUD 項(xiàng)目。如果完全沒有項(xiàng)目經(jīng)驗(yàn)先把重心放在 Java 基礎(chǔ)和 Spring Boot 實(shí)戰(zhàn)上不要一上來(lái)就啃并發(fā)和分布式。2. Java 基礎(chǔ)和并發(fā)編程先建立答題框架Java 基礎(chǔ)是后端面試的第一關(guān)。這個(gè)環(huán)節(jié)看起來(lái)簡(jiǎn)單但很容易暴露出“只看過(guò)博客、沒看過(guò)源碼”的問(wèn)題。比如問(wèn) HashMap 底層結(jié)構(gòu)很多人能答出“數(shù)組加鏈表加紅黑樹”但再問(wèn)一句“什么時(shí)候轉(zhuǎn)紅黑樹為什么閾值是 8”就答不上來(lái)了。2.1 HashMap、ArrayList、String 這三類題怎么準(zhǔn)備后端八股文里集合類題目出現(xiàn)頻率極高。準(zhǔn)備時(shí)要按“結(jié)構(gòu)、擴(kuò)容、線程安全、實(shí)際坑點(diǎn)”四層來(lái)答而不是只背結(jié)論。以 HashMap 為例結(jié)構(gòu)數(shù)組 鏈表 紅黑樹。默認(rèn)初始容量 16負(fù)載因子 0.75。擴(kuò)容當(dāng)元素?cái)?shù)量超過(guò)容量乘以負(fù)載因子時(shí)觸發(fā)擴(kuò)容重新計(jì)算哈希并遷移數(shù)據(jù)。樹化鏈表長(zhǎng)度超過(guò) 8 且數(shù)組長(zhǎng)度超過(guò) 64 時(shí)轉(zhuǎn)為紅黑樹。閾值為 8 是因?yàn)樵陔S機(jī)哈希情況下鏈表長(zhǎng)度達(dá)到 8 的概率已經(jīng)很低這個(gè)值是時(shí)間和空間的權(quán)衡結(jié)果。坑點(diǎn)JDK 7 和 JDK 8 的擴(kuò)容實(shí)現(xiàn)有差異并發(fā)環(huán)境下 HashMap 的 put 操作不是線程安全的多線程擴(kuò)容時(shí)可能出現(xiàn)循環(huán)鏈表導(dǎo)致 CPU 100%。實(shí)際項(xiàng)目中并發(fā)場(chǎng)景不要用 HashMap用 ConcurrentHashMap。再比如 ArrayList 和 LinkedList 的對(duì)比不要只說(shuō)“ArrayList 查詢快、增刪慢LinkedList 增刪快、查詢慢”。更準(zhǔn)確的說(shuō)法是ArrayList 基于動(dòng)態(tài)數(shù)組支持隨機(jī)訪問(wèn)尾部插入在容量足夠時(shí)是 O(1)中間插入需要移動(dòng)元素LinkedList 基于雙向鏈表頭尾插入刪除是 O(1)但按索引訪問(wèn)是 O(n)。實(shí)際開發(fā)中絕大多數(shù)列表場(chǎng)景用 ArrayList 就夠LinkedList 的優(yōu)勢(shì)場(chǎng)景很有限。String 相關(guān)的高頻題包括 String 不可變性、StringBuilder 和 StringBuffer 區(qū)別、字符串常量池。答題時(shí)要能說(shuō)清楚“String 為什么設(shè)計(jì)成不可變”緩存 hash、安全、線程安全、常量池復(fù)用但這些都要結(jié)合場(chǎng)景說(shuō)明不要一上來(lái)就背四個(gè)優(yōu)點(diǎn)。2.2 并發(fā)編程的復(fù)習(xí)重點(diǎn)不是背 API是拆解三個(gè)核心問(wèn)題并發(fā)編程是 Java 后端面試的難點(diǎn)也是最容易拉開差距的部分。我的復(fù)習(xí)建議是圍繞三個(gè)核心問(wèn)題展開原子性、可見性、有序性。所有并發(fā)問(wèn)題最終都會(huì)落到這三個(gè)問(wèn)題上??梢娦詖olatile 可以保證變量修改對(duì)線程可見但它不保證原子性。原子性synchronized、Lock、Atomic 類解決復(fù)合操作的原子性問(wèn)題。有序性指令重排問(wèn)題volatile 通過(guò)內(nèi)存屏障禁止重排synchronized 通過(guò)加鎖串行化執(zhí)行。線程池是另一個(gè)高頻考點(diǎn)。不要只背 ThreadPoolExecutor 的七個(gè)參數(shù)要能回答這幾個(gè)問(wèn)題核心線程數(shù)怎么設(shè)置CPU 密集型任務(wù)通常設(shè)置為 CPU 核數(shù)加一IO 密集型任務(wù)可以設(shè)置得更大因?yàn)榫€程在等待 IO 時(shí)不會(huì)占用 CPU。隊(duì)列滿了怎么辦四種拒絕策略分別是 AbortPolicy、CallerRunsPolicy、DiscardPolicy、DiscardOldestPolicy。實(shí)際項(xiàng)目中更常見的是自定義拒絕策略比如把任務(wù)寫入 MQ 或本地表后續(xù)補(bǔ)償處理。為什么不允許使用 Executors 創(chuàng)建線程池因?yàn)?FixedThreadPool 和 SingleThreadExecutor 的隊(duì)列是無(wú)界隊(duì)列任務(wù)堆積時(shí)可能導(dǎo)致 OOMCachedThreadPool 最大線程數(shù)接近無(wú)限可能創(chuàng)建大量線程耗盡資源。ThreadLocal 也是一個(gè)容易被追問(wèn)的點(diǎn)。除了“每個(gè)線程有自己的副本”這個(gè)表面解釋還要能說(shuō)清楚 ThreadLocalMap 的 key 是弱引用、value 是強(qiáng)引用在線程池場(chǎng)景下如果使用后不調(diào)用 remove可能造成內(nèi)存泄漏。這個(gè)點(diǎn)結(jié)合 OOM 排查來(lái)答會(huì)顯得更有實(shí)戰(zhàn)感。3. JVM 和線上問(wèn)題排查從背結(jié)論到看排查過(guò)程JVM 相關(guān)內(nèi)容在面試中占的比例不一定最高但一旦被問(wèn)到通常都是深入追問(wèn)。很多候選人能背出堆內(nèi)存分為新生代和老年代能說(shuō)出垃圾回收算法有標(biāo)記復(fù)制、標(biāo)記清除、標(biāo)記整理但被問(wèn)到“線上服務(wù)頻繁 Full GC 怎么排查”時(shí)就只會(huì)說(shuō)“加內(nèi)存”或“換垃圾回收器”。3.1 JVM 內(nèi)存區(qū)域和垃圾回收按“為什么這樣劃分”來(lái)記JVM 內(nèi)存區(qū)域不是八個(gè)名詞的列表它反映的是 JVM 在管理 Java 對(duì)象生命周期時(shí)的策略。堆所有線程共享存放對(duì)象實(shí)例是垃圾回收的主要區(qū)域。堆內(nèi)部又分為新生代和老年代新生代再分為 Eden 區(qū)、From Survivor、To Survivor。為什么要分代因?yàn)榻^大多數(shù)對(duì)象生命周期很短分代之后可以用不同的回收算法處理不同生命周期對(duì)象提高回收效率。虛擬機(jī)棧線程私有每個(gè)方法調(diào)用對(duì)應(yīng)一個(gè)棧幀棧幀里有局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法出口。棧溢出常見原因是遞歸調(diào)用過(guò)深或方法棧幀過(guò)大。方法區(qū)存儲(chǔ)類信息、常量、靜態(tài)變量。JDK 8 之后改為元空間使用本地內(nèi)存避免永久代 OOM。程序計(jì)數(shù)器線程私有記錄當(dāng)前線程執(zhí)行的字節(jié)碼行號(hào)。垃圾回收部分要能講清楚新生代使用復(fù)制算法因?yàn)樾聦?duì)象存活率低復(fù)制成本小老年代使用標(biāo)記整理或標(biāo)記清除因?yàn)槔夏甏鷮?duì)象存活率高復(fù)制代價(jià)大。CMS、G1、ZGC 的區(qū)別也要有個(gè)基本認(rèn)知尤其是 G1 在 JDK 8 和 JDK 11 之后都可能是默認(rèn)收集器要能說(shuō)明它把堆分成多個(gè) Region可以設(shè)置目標(biāo)停頓時(shí)間。3.2 OOM 和 Full GC 排查思路要背一套標(biāo)準(zhǔn)流程熱點(diǎn)詞里有“java: outofmemoryerror: insufficient memory”說(shuō)明網(wǎng)上一搜就能看到大量這類報(bào)錯(cuò)。面試官問(wèn) OOM 時(shí)真正想聽的是你遇到類似現(xiàn)象時(shí)會(huì)怎么處理。我建議把排查流程固定成五步先確認(rèn)現(xiàn)象服務(wù)是啟動(dòng)時(shí)直接報(bào)錯(cuò)還是運(yùn)行一段時(shí)間后報(bào)錯(cuò)是頻繁 Full GC還是直接 OOM 崩潰拿到堆轉(zhuǎn)儲(chǔ)文件在啟動(dòng)參數(shù)中加上-XX:HeapDumpOnOutOfMemoryError讓 JVM 在 OOM 時(shí)自動(dòng)導(dǎo)出 dump 文件。如果沒有這個(gè)參數(shù)重啟前先再觸發(fā)一次或通過(guò) jmap 手動(dòng)導(dǎo)。用 MAT 或 JVisualVM 分析 dump看哪個(gè)對(duì)象占用了大量堆內(nèi)存是業(yè)務(wù)對(duì)象、緩存對(duì)象、還是線程內(nèi)部塞了超大集合。結(jié)合線程棧定位代碼位置線程棧能看到當(dāng)前線程執(zhí)行到哪一行代碼如果大量線程卡在同一個(gè)方法里優(yōu)先看那個(gè)方法是不是在循環(huán)創(chuàng)建對(duì)象、讀取大文件、或往集合里無(wú)限添加數(shù)據(jù)。修復(fù)并驗(yàn)證常見的修復(fù)方式包括限制集合大小、改用分批處理、優(yōu)化 SQL 避免一次性加載過(guò)多數(shù)據(jù)、使用合適的緩存過(guò)期策略、排查 ThreadLocal 是否未清理。Full GC 頻繁的情況也一樣不能一上來(lái)就調(diào)大堆內(nèi)存。堆內(nèi)存調(diào)大可能讓單次 GC 時(shí)間更長(zhǎng)。正確順序是先看 GC 日志用 jstat 觀察 Eden 區(qū)、老年代、GC 次數(shù)的變化趨勢(shì)再判斷是內(nèi)存泄漏導(dǎo)致老年代持續(xù)增長(zhǎng)還是對(duì)象分配速率過(guò)高導(dǎo)致頻繁晉升。注意JVM 調(diào)優(yōu)不是面試重點(diǎn)的背誦項(xiàng)而是排查鏈路的輔助手段。不要提-Xmx和-Xms就停住要能說(shuō)出為什么會(huì)設(shè)置相等原因是可以避免運(yùn)行期堆大小動(dòng)態(tài)調(diào)整帶來(lái)的性能抖動(dòng)。4. Spring 和 Spring Boot 核心原理用一條 Bean 生命周期線串起來(lái)Spring 相關(guān)問(wèn)題在 Java 后端面試?yán)飵缀醣乜?。高頻題集中在 IOC、AOP、Bean 生命周期、事務(wù)傳播機(jī)制、自動(dòng)裝配。這些內(nèi)容如果分開背會(huì)覺得很碎如果串起來(lái)可以形成一條線Bean 是怎么創(chuàng)建出來(lái)的創(chuàng)建過(guò)程中有哪些擴(kuò)展點(diǎn)Spring 如何把 AOP 能力織入對(duì)象事務(wù)如何通過(guò) AOP 生效。4.1 IOC 和 Bean 生命周期要能畫成時(shí)間線先回答最基礎(chǔ)的問(wèn)題IOC 是什么IOC 控制反轉(zhuǎn)核心思想是把對(duì)象的創(chuàng)建和依賴注入交給容器管理而不是在代碼里自己 new。這樣做的意義是解耦同時(shí)讓對(duì)象之間的依賴關(guān)系集中配置。Bean 生命周期可以按階段記實(shí)例化前BeanPostProcessor 的 postProcessBeforeInstantiation 有機(jī)會(huì)返回代理對(duì)象。實(shí)例化通過(guò)構(gòu)造器創(chuàng)建對(duì)象。屬性填充Autowired 和 Resource 在這個(gè)階段完成依賴注入。初始化前BeanPostProcessor 的 postProcessBeforeInitialization。初始化執(zhí)行 InitializingBean 的 afterPropertiesSet 和自定義 init-method。初始化后BeanPostProcessor 的 postProcessAfterInitializationAOP 代理通常在這個(gè)時(shí)機(jī)生成。使用中容器返回 Bean 給調(diào)用方。銷毀執(zhí)行 DisposableBean 的 destroy 方法和自定義 destroy-method。這個(gè)順序不用死記每個(gè)步驟的英文名但要把“實(shí)例化、屬性填充、初始化、代理生成”這四件事搞清楚。面試官問(wèn)“Spring 里怎么在 Bean 初始化前后做點(diǎn)事”你能答出 InitializingBean、init-method、BeanPostProcessor 就已經(jīng)過(guò)了基礎(chǔ)關(guān)。如果還能補(bǔ)充“BeanPostProcessor 在多個(gè) Bean 初始化時(shí)都會(huì)被調(diào)用需要判斷 beanName 來(lái)避免影響所有對(duì)象”那就是加分項(xiàng)。4.2 AOP 和事務(wù)要能解釋“代理失效”這類坑AOP 的核心概念是切面、通知、切點(diǎn)。Spring 默認(rèn)使用動(dòng)態(tài)代理如果目標(biāo)類實(shí)現(xiàn)了接口用 JDK 動(dòng)態(tài)代理如果沒有實(shí)現(xiàn)接口用 CGLIB 生成子類代理。面試?yán)锍R姷淖穯?wèn)是“Spring 事務(wù)什么情況下會(huì)失效”。這個(gè)問(wèn)題很值得反復(fù)練因?yàn)樗瑫r(shí)考察了 AOP 原理和事務(wù)傳播機(jī)制。常見的失效場(chǎng)景有方法不是 publicSpring 默認(rèn)不代理非 public 方法。自身調(diào)用也就是同類內(nèi)部調(diào)用比如一個(gè)方法調(diào)用了另一個(gè)帶Transactional的方法代理對(duì)象沒有參與事務(wù)不生效。異常被 catch 住事務(wù)感知不到異常自然不會(huì)回滾。拋出的是非 RuntimeException但事務(wù)默認(rèn)只對(duì) RuntimeException 回滾。數(shù)據(jù)庫(kù)引擎不支持事務(wù)比如 MySQL 的 MyISAM 引擎就不支持事務(wù)。事務(wù)傳播機(jī)制也是必背內(nèi)容重點(diǎn)記 PROPAGATION_REQUIRED 和 PROPAGATION_REQUIRES_NEW。前者表示當(dāng)前有事務(wù)就加入沒有就新建后者表示無(wú)論如何都新建一個(gè)事務(wù)。還有一個(gè)容易混淆的 PROPAGATION_NESTED它是嵌套事務(wù)回滾時(shí)可以只回滾到保存點(diǎn)不會(huì)影響外部事務(wù)。Spring Boot 自動(dòng)裝配的考點(diǎn)集中在SpringBootApplication、EnableAutoConfiguration、spring.factories或AutoConfiguration.imports文件、條件注解。復(fù)習(xí)時(shí)要把“為什么平時(shí)不配置也能用 RedisTemplate”這個(gè)現(xiàn)象解釋清楚。5. MySQL 和 Redis堅(jiān)持“索引優(yōu)先、隔離級(jí)別為主、緩存問(wèn)題為戰(zhàn)”數(shù)據(jù)庫(kù)和緩存是后端面試的重頭戲。很多候選人 MySQL 背了一大堆但面試官問(wèn)一道慢 SQL 優(yōu)化題就不知道從哪開始Redis 背了五種數(shù)據(jù)結(jié)構(gòu)但項(xiàng)目里只當(dāng)作緩存用一問(wèn)集群和分布式鎖就露怯。這一部分建議按“會(huì)用、能排查、能設(shè)計(jì)”三層來(lái)復(fù)習(xí)。5.1 MySQL 索引和 SQL 優(yōu)化從執(zhí)行計(jì)劃開始MySQL 高頻考點(diǎn)包括索引結(jié)構(gòu)、最左前綴原則、回表、覆蓋索引、索引失效場(chǎng)景、事務(wù)隔離級(jí)別、MVCC、鎖。索引結(jié)構(gòu)默認(rèn)是 B 樹。要能說(shuō)清楚為什么選 B 樹而不是 B 樹、哈希表B 樹非葉子節(jié)點(diǎn)不存儲(chǔ)數(shù)據(jù)單節(jié)點(diǎn)能存更多索引項(xiàng)樹高度更低磁盤 IO 次數(shù)更少葉子節(jié)點(diǎn)通過(guò)雙向鏈表連接適合范圍查詢。哈希索引適合等值查詢但不支持范圍查詢和排序。最左前綴原則要求聯(lián)合索引(a, b, c)能命中a、a,b、a,b,c但直接查b或c走不了這個(gè)索引。復(fù)習(xí)時(shí)要結(jié)合一個(gè)具體例子比如查詢條件里寫了a和c能走索引但c的過(guò)濾效果要打折扣。索引失效場(chǎng)景很常見但一定要結(jié)合原因去記對(duì)索引列做函數(shù)操作或隱式類型轉(zhuǎn)換導(dǎo)致無(wú)法使用索引。聯(lián)合索引不滿足最左前綴。使用like時(shí)通配符出現(xiàn)在最前面比如like %abc。優(yōu)化器判斷全表掃描比走索引更快可能選擇不走索引。這種情況不是索引失效而是優(yōu)化器代價(jià)評(píng)估結(jié)果。SQL 優(yōu)化不要只背概念給自己固定一套排查鏈路先慢查詢?nèi)罩菊业骄唧w SQL。用EXPLAIN看執(zhí)行計(jì)劃重點(diǎn)看 type、key、rows、Extra。type 從 system、const、eq_ref、ref、range、index、ALL 依次變差如果看到 ALL 就要想辦法加索引或改寫 SQL。Extra 出現(xiàn) Using temporary 或 Using filesort說(shuō)明需要臨時(shí)表或文件排序考慮優(yōu)化排序字段和索引。再看數(shù)據(jù)量和業(yè)務(wù)場(chǎng)景決定是加索引、改 SQL 結(jié)構(gòu)、分頁(yè)優(yōu)化還是歸檔歷史數(shù)據(jù)。MySQL 事務(wù)隔離級(jí)別要按事務(wù)并發(fā)問(wèn)題來(lái)記臟讀、不可重復(fù)讀、幻讀對(duì)應(yīng)不同的隔離級(jí)別。MySQL 默認(rèn)隔離級(jí)別是 Repeatable Read它通過(guò) MVCC 解決普通讀的不可重復(fù)讀和幻讀問(wèn)題通過(guò)間隙鎖和臨鍵鎖解決當(dāng)前讀的幻讀和寫沖突問(wèn)題。把 MVCC 的 undo log、ReadView、三個(gè)隱藏列連起來(lái)講比單純背概念更有說(shuō)服力。5.2 Redis 高頻問(wèn)題用“緩存三大坑 持久化 分布式鎖”三塊來(lái)收Redis 的數(shù)據(jù)結(jié)構(gòu)題不難難的是方案設(shè)計(jì)。面試中問(wèn)得最多的三個(gè)方案類問(wèn)題是緩存穿透、緩存擊穿、緩存雪崩。緩存穿透請(qǐng)求查一個(gè)不存在的數(shù)據(jù)緩存里沒有每次都要打到數(shù)據(jù)庫(kù)。解決辦法是緩存空值并設(shè)置較短過(guò)期時(shí)間或者使用布隆過(guò)濾器攔截。緩存擊穿一個(gè)熱點(diǎn) key 過(guò)期大量請(qǐng)求同時(shí)打到數(shù)據(jù)庫(kù)。解決辦法是熱點(diǎn) key 不設(shè)置過(guò)期時(shí)間或者加互斥鎖只讓一個(gè)請(qǐng)求去重建緩存。緩存雪崩大量 key 在同一時(shí)間段過(guò)期或者 Redis 實(shí)例宕機(jī)。解決辦法是過(guò)期時(shí)間加隨機(jī)值接口層做限流降級(jí)Redis 做高可用比如主從加哨兵或集群模式。Redis 持久化也要能講清楚 RDB 和 AOF 的區(qū)別。RDB 是快照適合備份和恢復(fù)但可能丟失最后一次快照之后的數(shù)據(jù)AOF 追加寫日志數(shù)據(jù)完整度高但文件大、恢復(fù)慢?,F(xiàn)在常見做法是 RDB 做主文件AOF 做數(shù)據(jù)補(bǔ)充。面試時(shí)要能結(jié)合“數(shù)據(jù)丟失容忍度”來(lái)解釋為什么生產(chǎn)環(huán)境需要兩種配合。分布式鎖也是常客。不要把“SETNX 加鎖Del 釋放鎖”當(dāng)成完整答案。一個(gè)完整的分布式鎖方案至少要考慮幾個(gè)問(wèn)題加鎖時(shí)設(shè)置過(guò)期時(shí)間防止持有鎖的線程崩潰導(dǎo)致死鎖。釋放鎖時(shí)先比較 value 是否是自己加的鎖防止誤刪別的線程的鎖。鎖過(guò)期時(shí)間不能太短否則業(yè)務(wù)還沒執(zhí)行完鎖就過(guò)期了另一個(gè)線程拿到鎖產(chǎn)生并發(fā)執(zhí)行問(wèn)題。如果要更穩(wěn)定可以引入 Redisson 的看門狗機(jī)制自動(dòng)續(xù)期。6. 消息隊(duì)列和分布式基礎(chǔ)選一個(gè)主流組件做深度準(zhǔn)備消息隊(duì)列內(nèi)容多且雜3 天速刷不建議同時(shí)準(zhǔn)備 Kafka、RocketMQ、RabbitMQ。從 Java 后端面試頻率來(lái)看Kafka 和 RabbitMQ 最常見如果你投的是互聯(lián)網(wǎng)公司Kafka 優(yōu)先級(jí)更高。我的建議是主攻一個(gè)另一個(gè)只做對(duì)比性了解。6.1 消息隊(duì)列高頻考點(diǎn)圍繞四個(gè)問(wèn)題展開無(wú)論哪個(gè) MQ面試題基本繞不開可靠性、順序性、重復(fù)消費(fèi)、消息積壓這四個(gè)問(wèn)題??煽啃陨a(chǎn)者端確認(rèn)機(jī)制、Broker 持久化、消費(fèi)者端手動(dòng)確認(rèn)。Kafka 里對(duì)應(yīng)的是 acks 參數(shù)、副本機(jī)制、消費(fèi)者 offset 提交方式。順序性全局順序很難做到通常只保證分區(qū)內(nèi)順序。Kafka 通過(guò)分區(qū)內(nèi)單線程消費(fèi)來(lái)保證順序但要注意業(yè)務(wù)上如何把相關(guān)消息路由到同一個(gè)分區(qū)。重復(fù)消費(fèi)消費(fèi)端要做冪等處理。常見做法是消費(fèi)時(shí)查重用唯一業(yè)務(wù)鍵判斷是否處理過(guò)或者把結(jié)果寫入具備唯一約束的存儲(chǔ)。消息積壓先擴(kuò)容消費(fèi)者數(shù)量但要注意分區(qū)數(shù)和消費(fèi)者組的關(guān)系消費(fèi)者數(shù)量超過(guò)分區(qū)數(shù)并不能提升消費(fèi)速度。更穩(wěn)妥的做法是臨時(shí)新起消費(fèi)者服務(wù)把消息快速轉(zhuǎn)發(fā)到新的 topic再增加消費(fèi)者處理。6.2 分布式知識(shí)不要貪多把 CAP、分布式事務(wù)、冪等設(shè)計(jì)講透分布式相關(guān)的高頻題包括 CAP、BASE、分布式事務(wù)、接口冪等、負(fù)載均衡策略。這些內(nèi)容如果有時(shí)間可以系統(tǒng)看沒時(shí)間就先把高頻題的答題結(jié)構(gòu)固定下來(lái)。CAP 不要只背“一致性、可用性、分區(qū)容錯(cuò)性三者不可兼得”。要能結(jié)合場(chǎng)景說(shuō)明分布式系統(tǒng)必然存在網(wǎng)絡(luò)分區(qū)P 是必須保證的所以實(shí)際是在 C 和 A 之間取舍。ZooKeeper 偏向 CPEureka 偏向 AP這就是為什么注冊(cè)中心選型會(huì)有不同方案。分布式事務(wù)常見方案包括 2PC、TCC、本地消息表、事務(wù)消息。2PC 有同步阻塞和協(xié)調(diào)者單點(diǎn)問(wèn)題TCC 需要業(yè)務(wù)方實(shí)現(xiàn) Try、Confirm、Cancel 三個(gè)方法侵入性強(qiáng)事務(wù)消息適合最終一致性場(chǎng)景比如訂單創(chuàng)建后發(fā)消息給積分服務(wù)。面試時(shí)能把“為什么不用 2PC 解決所有問(wèn)題”講清楚證明你真正理解過(guò)。接口冪等也是后端面試的高頻場(chǎng)景題。設(shè)計(jì)時(shí)要選一個(gè)唯一鍵比如訂單號(hào)或用戶 ID 加業(yè)務(wù)類型。最簡(jiǎn)單的方式是數(shù)據(jù)庫(kù)唯一索引插入前查一次唯一鍵或者利用數(shù)據(jù)庫(kù)的 INSERT 語(yǔ)句在沖突時(shí)返回影響行數(shù) 0再?zèng)Q定是否走已處理邏輯。更復(fù)雜的場(chǎng)景可以引入狀態(tài)機(jī)在狀態(tài)流轉(zhuǎn)時(shí)加版本號(hào)判斷防止重復(fù)提交覆蓋數(shù)據(jù)。7. 項(xiàng)目經(jīng)驗(yàn)和八股文怎么串起來(lái)用“場(chǎng)景題”做模擬3 天速刷最容易忽略的就是八股文和項(xiàng)目經(jīng)驗(yàn)的結(jié)合。面試官經(jīng)常在八股文答完之后追問(wèn)“你項(xiàng)目里怎么用的”如果你只背了理論現(xiàn)場(chǎng)很難組織出一個(gè)連貫回答。我建議在第二天晚上和第三天上午專門做“項(xiàng)目場(chǎng)景串聯(lián)”練習(xí)。7.1 把每個(gè)高頻知識(shí)點(diǎn)映射到自己的項(xiàng)目場(chǎng)景以常見的電商型或管理后臺(tái)型項(xiàng)目為例可以做一個(gè)簡(jiǎn)單映射用戶登錄模塊對(duì)應(yīng) Spring Security 或 JWT、Redis 存儲(chǔ) token、密碼加密、Session 分布式問(wèn)題。商品列表接口對(duì)應(yīng) MySQL 索引優(yōu)化、多級(jí)緩存、緩存穿透和擊穿處理。訂單創(chuàng)建對(duì)應(yīng)事務(wù)邊界、分布式事務(wù)、冪等設(shè)計(jì)、消息隊(duì)列異步通知。數(shù)據(jù)統(tǒng)計(jì)報(bào)表對(duì)應(yīng)慢 SQL 優(yōu)化、分頁(yè)查詢優(yōu)化、異步任務(wù)、定時(shí)任務(wù)。文件上傳或?qū)С鰧?duì)應(yīng)內(nèi)存占用、磁盤空間、線程池異步處理、OOM 風(fēng)險(xiǎn)。服務(wù)監(jiān)控對(duì)應(yīng) JVM 指標(biāo)、GC 日志、接口耗時(shí)、日志鏈路追蹤。做這種映射時(shí)不需要項(xiàng)目真的很復(fù)雜哪怕你做過(guò)一個(gè)簡(jiǎn)單 CRUD 項(xiàng)目也能把索引優(yōu)化和事務(wù)傳播機(jī)制掛上去。關(guān)鍵是把理論落到真實(shí)上下文里讓面試官覺得你是真的處理過(guò)問(wèn)題而不是只會(huì)復(fù)制粘貼。7.2 模擬問(wèn)答時(shí)按 STAR 或“現(xiàn)象-原因-方案-結(jié)果”組織場(chǎng)景題的回答結(jié)構(gòu)我建議固定成四步描述背景這個(gè)功能是什么用戶量或數(shù)據(jù)量大概什么級(jí)別。說(shuō)明問(wèn)題遇到了什么現(xiàn)象比如接口慢、消息重復(fù)消費(fèi)、內(nèi)存上漲。分析原因怎么定位的看了什么日志、什么監(jiān)控、什么執(zhí)行計(jì)劃。給出方案做了哪些改動(dòng)改完之后怎么驗(yàn)證的效果如何。這里要特別注意不要編造自己沒有做過(guò)的經(jīng)歷。如果確實(shí)沒遇到過(guò)高并發(fā)承認(rèn)自己處理過(guò)的是小規(guī)模請(qǐng)求但把排查思路完整講清楚面試官更看重思路是否合理。7.3 第三天上午做一次“高頻題口述測(cè)試”口述測(cè)試很簡(jiǎn)單找一個(gè)朋友或自己對(duì)著一張白紙隨機(jī)抽 20 道題每題限制 3 分鐘講完講的時(shí)候錄音。講完回聽重點(diǎn)檢查有沒有出現(xiàn)以下幾種情況講著講著卡住說(shuō)明知識(shí)點(diǎn)沒有形成鏈路回去重新整理這段內(nèi)容。說(shuō)了很多但沒提到原理只回答了是什么沒回答為什么。沒有結(jié)合項(xiàng)目或場(chǎng)景說(shuō)明停留在名詞解釋層面。被追問(wèn)“如果”場(chǎng)景時(shí)反應(yīng)慢說(shuō)明邊界知識(shí)準(zhǔn)備不足??谑鰷y(cè)試比看題單有用得多。因?yàn)槊嬖嚂r(shí)是邊說(shuō)邊組織思路不是默寫答案。如果你能在一個(gè)小時(shí)內(nèi)把 20 道高頻題連續(xù)講完語(yǔ)速穩(wěn)定每個(gè)題能給出一個(gè)核心結(jié)論和一個(gè)項(xiàng)目案例基本就達(dá)到速刷目標(biāo)了。8. 速刷時(shí)間表和避坑清單最后留幾個(gè)排查角度3 天的時(shí)間安排不需要做到分鐘級(jí)但要明確每天的核心產(chǎn)出。第一天能力自測(cè)、收集高頻題、整理 Java 基礎(chǔ)、并發(fā)、JVM 的答題框架。重點(diǎn)是把“不會(huì)但背過(guò)”的知識(shí)點(diǎn)變成“能講出來(lái)”的結(jié)構(gòu)化表達(dá)。第二天攻克 Spring 系列、MySQL、Redis。白天整理知識(shí)點(diǎn)晚上按項(xiàng)目場(chǎng)景做映射把理論掛到真實(shí)功能點(diǎn)上。第三天模擬問(wèn)答、口述測(cè)試、補(bǔ)漏。選取 20 道高頻題進(jìn)行全流程模擬并對(duì)不熟悉的方向做最后一輪查缺補(bǔ)漏。這個(gè)安排適合時(shí)間緊、基礎(chǔ)中等的讀者。如果你的 Java 基礎(chǔ)還比較薄弱建議先把 Java 基礎(chǔ)、MySQL、Spring Boot 實(shí)戰(zhàn)跑通再考慮并發(fā)和分布式。如果你的目標(biāo)是高級(jí)崗位那就不能只指望三天速刷至少要留一周來(lái)深挖 JVM 調(diào)優(yōu)、消息隊(duì)列架構(gòu)和分布式方案。8.1 速刷時(shí)最常見的四個(gè)錯(cuò)誤做法第一只背題目不看原理。很多題目背下來(lái)很容易比如“HashMap 是數(shù)組加鏈表加紅黑樹”但面試官只要多問(wèn)一句“為什么閾值是 8”就暴露了。背題時(shí)一定要多問(wèn)自己一層“為什么”。第二只看知識(shí)點(diǎn)不做串聯(lián)。八股文的高頻考點(diǎn)不是孤立的Java 并發(fā)和線程池會(huì)關(guān)聯(lián)到 OOMRedis 緩存問(wèn)題會(huì)關(guān)聯(lián)到接口性能和項(xiàng)目方案Spring 事務(wù)會(huì)關(guān)聯(lián)到 AOP。用關(guān)聯(lián)關(guān)系復(fù)習(xí)比按零散題單背效率高很多。第三忽略工程化基礎(chǔ)。除了八股文后端面試還會(huì)涉及 Git、Linux、IDEA 使用、接口聯(lián)調(diào)、日志排查、前后端分離項(xiàng)目部署。熱點(diǎn)詞里出現(xiàn)的“idea 如何進(jìn)行前后端開發(fā)”“如何搭建 Java 后端項(xiàng)目”“前后端分離項(xiàng)目實(shí)戰(zhàn)”都說(shuō)明工程化能力會(huì)被問(wèn)到不一定作為獨(dú)立題目但會(huì)在項(xiàng)目經(jīng)驗(yàn)環(huán)節(jié)被反復(fù)考察。如果 3 天時(shí)間里還有空余至少要把“本地啟動(dòng)一個(gè) Spring Boot 項(xiàng)目、連接數(shù)據(jù)庫(kù)、提供 REST 接口、用 IDEA 調(diào)試”這條鏈路練熟練。第四標(biāo)題里的“通過(guò)率 99%”不要當(dāng)真。沒有哪套題單能保證通過(guò)率面試是綜合考察八股文只占一部分。真正起作用的是你能否在有限時(shí)間里把自己的知識(shí)結(jié)構(gòu)整理清楚并且讓面試官聽到一個(gè)完整、自洽的回答過(guò)程。8.2 現(xiàn)場(chǎng)答題時(shí)最值得養(yǎng)成的三個(gè)習(xí)慣第一個(gè)習(xí)慣是“先給結(jié)論再展開”。面試官問(wèn)“Redis 緩存穿透怎么解決”先答“緩存空值或布隆過(guò)濾器”再補(bǔ)一句“我項(xiàng)目里用的是緩存空值因?yàn)椴悸∵^(guò)濾器有誤判和更新成本”。兩句話就把核心答案和項(xiàng)目細(xì)節(jié)都給出來(lái)了。第二個(gè)習(xí)慣是“主動(dòng)說(shuō)邊界”。答完一個(gè)方案后主動(dòng)補(bǔ)一句“這個(gè)方案的缺點(diǎn)是……如果并發(fā)量更大我會(huì)考慮……”。這會(huì)讓面試官覺得你是有實(shí)戰(zhàn)判斷的人而不是考試機(jī)器。第三個(gè)習(xí)慣是“答不上來(lái)就拆步驟”。遇到不會(huì)的問(wèn)題先不要慌把題目拆成自己知道的部分嘗試回答。比如問(wèn)“ZGC 的染色指針原理”如果不知道細(xì)節(jié)可以說(shuō)“我對(duì) ZGC 的并發(fā)實(shí)現(xiàn)沒有深入研究但我了解 G1 的 Region 劃分和停頓預(yù)測(cè)模型ZGC 相比 G1 的主要優(yōu)勢(shì)是停頓時(shí)間更短我平時(shí)主要用 G1所以先把熟悉的部分講清楚。”這種情況比直接說(shuō)“不會(huì)”好很多。8.3 后面幾天如果還有時(shí)間優(yōu)先補(bǔ)哪些方向如果 3 天速刷之后還有多余時(shí)間優(yōu)先補(bǔ)四個(gè)方向設(shè)計(jì)模式、Linux 常用命令、網(wǎng)絡(luò)協(xié)議、線上問(wèn)題排查案例。設(shè)計(jì)模式重點(diǎn)準(zhǔn)備單例、工廠、策略、模板方法、觀察者因?yàn)檫@些在 Spring 源碼和項(xiàng)目代碼中經(jīng)常出現(xiàn)。Linux 至少掌握日志查看、進(jìn)程和端口查看、磁盤空間檢查、文件權(quán)限修改。網(wǎng)絡(luò)協(xié)議重點(diǎn)放在 HTTP、TCP 三次握手四次揮手、HTTPS 握手過(guò)程。線上問(wèn)題排查案例可以和 JVM 部分合并準(zhǔn)備一個(gè)“接口突然變慢怎么排查”的整體思路。這些內(nèi)容不一定每天都會(huì)考到但你面試時(shí)如果能主動(dòng)提一句“這個(gè)場(chǎng)景我線上遇到類似問(wèn)題時(shí)會(huì)先看接口耗時(shí)和 GC 日志”整體印象會(huì)好很多。我個(gè)人更建議把這次速刷當(dāng)成一次知識(shí)體檢而不是賭題庫(kù)。3 天時(shí)間能做的是把 Java 后端核心高頻題的系統(tǒng)框架搭起來(lái)把容易卡殼的知識(shí)點(diǎn)變成能說(shuō)出口的結(jié)構(gòu)化表達(dá)再用項(xiàng)目場(chǎng)景做一次串聯(lián)。真正決定面試結(jié)果的不是背了多少題而是面對(duì)一個(gè)不確定的問(wèn)題時(shí)能不能穩(wěn)定地給出有邏輯、有邊界、有判斷的答案。