背景
985畢業(yè)至今剛好一年,我曾做過兩三個月的測試感覺不是很合適,后面選擇從事后端開發(fā),還挺香?,F(xiàn)在已經(jīng)進入秋招的提前批了,想著去大廠試試水,就去了騰訊,整個一面下來我整個人都傻了,表示懷疑人生...沒想過一面就能問這么多,瘋狂轟炸,連環(huán)50問,不得停歇。感覺我這輩子都不會忘記這次面試經(jīng)歷了,給大家看看我的面試過程,我想,恐怕你也會表示同感。

> 注意:此次面試采用的是電話面試,我會復(fù)述全部的面試過程以及個人的回答情況和反思與總結(jié),并且會分享我的面試學(xué)習(xí)的刷題筆記,有需要的添加小助理vx:mxzFAFAFA即可!!**
我的面試過程(歷經(jīng)70mins)
1、個人履歷簡述
2、項目簡述
- 主要突出重難點,我bb了一堆業(yè)務(wù)邏輯結(jié)果人家都不感興趣
3、SpringAOP實現(xiàn)
- JDK動態(tài)代理:實現(xiàn)Invocationhandler接口,本質(zhì)上是new一個繼承了所有類上Interface的Proxy對象,然后通過method.invoke進行調(diào)用
- CGLib動態(tài)代理:在內(nèi)存中動態(tài)生成子類對原對象進行代理,無法代理final類以及方法
- 共同限制:無法代理到當前class當中this引用的嵌套方法
4、AOP用的哪種?
- 默認用的JDK動態(tài)代理
5、JDK動態(tài)代理以及CGLib動態(tài)代理性能比較
- JDK走的反射,會多一些反射調(diào)用的開銷(方法權(quán)限驗證、調(diào)用開銷等)
- CGLib需要創(chuàng)建新對象,在創(chuàng)建新對象上,即初始化時會多一些開銷
6、Java的線程池用過嗎,具體參數(shù)講一下
Java的線程池是一個三級存儲結(jié)構(gòu),線程先放入核心線程池,滿了之后放到緩存隊列當中,最后如果緩存隊列也滿了則擴容新線程,所以參數(shù)有:
- 核心線程數(shù)量
- 緩存隊列類型
- 最大線程數(shù)量
- 線程活躍時間
- 線程工廠方法(寫日志、重命名線程等)
7、線程池的Execute和Submit區(qū)別
- Execute執(zhí)行runnable,Submit可以執(zhí)行Future,我們一般用countDownLatch+Future來獲取所有的線程結(jié)果
8、繼續(xù)問,還有別的區(qū)別嗎?
- 不知道了,后續(xù)查了發(fā)現(xiàn)區(qū)別如下
- Execute會在運行期直接拋出異常,Submit之后在調(diào)用Future.get的時候才會拋出異常
9、線程池如何保證當前線程獲取池內(nèi)的worker的時候不產(chǎn)生爭用
- volatile的state標志這個worker有沒有被使用
10、volatile的特性
- 通過禁止指令重排序來保證內(nèi)存可見性,實際使用內(nèi)存屏障實現(xiàn)的
11、內(nèi)存屏障分幾種?
當時記不得了,回頭查了一下如下:
- LoadLoad屏障:對于這樣的語句Load1; LoadLoad; Load2,在Load2及后續(xù)讀取操作要讀取的數(shù)據(jù)被訪問前,保證Load1要讀取的數(shù)據(jù)被讀取完畢。
- StoreStore屏障:對于這樣的語句Store1; StoreStore; Store2,在Store2及后續(xù)寫入操作執(zhí)行前,保證Store1的寫入操作對其它處理器可見。
- LoadStore屏障:對于這樣的語句Load1; LoadStore; Store2,在Store2及后續(xù)寫入操作被刷出前,保證Load1要讀取的數(shù)據(jù)被讀取完畢。
- StoreLoad屏障:對于這樣的語句Store1; StoreLoad; Load2,在Load2及后續(xù)所有讀取操作執(zhí)行前,保證Store1的寫入對所有處理器可見。它的開銷是四種屏障中最大的。在大多數(shù)處理器的實現(xiàn)中,這個屏障是個萬能屏障,兼具其它三種內(nèi)存屏障的功能。
12、除了在volatile當中使用了內(nèi)存屏障,JAVA還有哪里使用了內(nèi)存屏障
- 這個真不知道,知道的小伙伴請在評論區(qū)指點一二
13、你之前講到了CountDownLatch,你知道它的內(nèi)部實現(xiàn)嗎
- 知道,用的AQS,在state=0的時候才允許所有等待的線程全部通過
14、簡單講一下AQS
AQS核心設(shè)計:
- 一個volatile int state的狀態(tài)值,使用volatile保證線程可見性,使用int來提供可重入的多資源能力
- 雙向隊列,首節(jié)點為執(zhí)行節(jié)點,可以根據(jù)執(zhí)行節(jié)點的Node信息判斷是ShareLock還是ExclusiveLock,會關(guān)聯(lián)一個執(zhí)行線程,來提供可重入的判斷
- 加鎖的時候若是公平鎖則嘗試CAS載入隊列,若是非公平鎖則直接入隊列
- 解鎖的時候直接喚醒后繼的第一個wait節(jié)點
15、加鎖之后AQS是如何響應(yīng)中斷的?
- 太細節(jié)了真不會,之前復(fù)習(xí)源碼沒看這么深(結(jié)束之后補漏洞)
16、OK問點別的,AQS存在什么實現(xiàn)呢?
- 用過的ReentranceLock、CountDownLatch
17、講講實現(xiàn)
- ReentranceLock通過判斷線程是否相同進行沖入
- CountDownLatch在state為0的時候才讓所有的await通過
18、聽說過ReadWriteLock嗎,你之前提到AQS當中只有一個State那你如何用一個State去支撐讀寫兩種狀態(tài)
- 一個state是Int,可以分高位給Read,低位給Write,就當個String用了
19、Int幾個字節(jié)
- 我居然回答了32個,應(yīng)該是32位,8位一個byte,共計四個byte
20、你們用過緩存嗎
- 沒有,但是用redis做了分布式鎖
21、你說說下分布式鎖怎么做的?
- 分布式鎖也是一個鎖,需要滿足幾個特性,1 可重入 2 可以識別加鎖的身份防止ABA問題 3 考慮是否需要續(xù)約
- key是所需要加上的鎖的業(yè)務(wù)資源唯一編碼,value是當前線程的uuid,uuid存在threadLocal內(nèi) 加鎖的時候用的jedis,先設(shè)一個過期時間,然后用ex,若不存在key則添加新key,若已經(jīng)存在則直接失敗
- 解鎖用的阿里云企業(yè)版的CAD(compareAndDelete),原子比較并解鎖,本質(zhì)是通過lua腳本進行的類似事務(wù)操作
22、除了redis還有什么可以做分布式鎖?
- Mysql、zookeeper等
23、如果讓你用Mysql做分布式鎖你怎么做
- 新建一張表,主鍵為需要鎖的鎖key,col1為線程uuid,col2為ttl時間
- 加鎖的時候在一個事務(wù)中選取當前key的record,若存在則判斷ttl,若不存在則直接可以插入
- 解鎖的時候直接把record刪除即可
- 起一個定時任務(wù)來遍歷表,清楚過期鍵防止無限膨脹
24、zookeeper了解嗎
- 一點點,攝入不深
25、那我們繼續(xù)聊聊Redis吧,Redis有什么數(shù)據(jù)結(jié)構(gòu)?
- List,Hash,Set,Zset,List
26、Zset怎么實現(xiàn)的?
- 跳表+map實現(xiàn)
27、什么是跳表?
- 常規(guī)鏈表只有一個next節(jié)點,跳表持有多個指向其他鏈表的指針,可以跨越式的進行查找,時間復(fù)雜度是logn
28、如果我要找一個score為A的節(jié)點應(yīng)該如何去找?
- 首先在map中找到對應(yīng)的node排名,然后根據(jù)排名在skiplist中進行查找
29、zrange是如何實現(xiàn)的?
- 這個沒想到不應(yīng)該,查了一下如下: ZRANGE key start stop [WITHSCORES],zrange 就是返回有序集 key 中,指定區(qū)間內(nèi)的成員,而跳表中的元素最下面的一層是有序的(上面的幾層就是跳表的索引),按照分數(shù)排序,我們只要找出 start 代表的元素,然后向前或者向后遍歷 M 次拉出所有數(shù)據(jù)即可,而找出 start 代表的元素,其實就是在跳表中找一個元素的時間復(fù)雜度。跳表中每個節(jié)點每一層都會保存到下一個節(jié)點的跨度,在尋找過程中可以根據(jù)跨度和來求當前的排名,所以查找過程是 O(log(N) 過程,加上遍歷 M 個元素,就是 O(log(N)+M),所以 redis 的 zrange 不會像 mysql 的 offset 有比較嚴重的性能問題。
30、Redis持久化
- RDB:快照存儲,可以選擇是否阻塞,使用場景在數(shù)據(jù)庫上下線、主備復(fù)制等情況中
- AOF:類似于binlog,每個里面都是一個寫事件,是優(yōu)先讀取的策略,支持多策略寫入(強同步、按時間刷盤、交由操作系統(tǒng)決定刷盤等),AOF為了防止文件膨脹也支持重寫
31、AOF重寫的時候會不會block主線程?
- 不會,沒有這個必要,起一個子線程重寫完畢之后把手頭的buffer在刷進去就行了
32、在載入的時候是怎么做的
- 本地起一個client直接讀取AOF重放其中的命令
33、Redis有哪些多機部署方案?
- 經(jīng)典的主備同步,通過RDB初始化備庫然后進行命令傳播 Sentinel,實際上是一種容災(zāi)機制 cluster,集群部署,使用多機占用slot的方式進行集群服務(wù)提供
34、在主備環(huán)境下,如果一個備庫中途斷鏈了,重新上線的時候怎么執(zhí)行同步?
- 主備各自維護一個寫入的Offset,對比差異之后在buffer中讀出丟失的命令并進行同步
35、如果備庫的offset過于落后已經(jīng)不在buffer當中了呢?
- 直接RDB重新同步 使用AOF來查找對應(yīng)offset的語句(這個是我猜的)
36、cluster如何做的故障轉(zhuǎn)移?
- 不知道,估計也是檢測到客觀下線然后paxos選主
37、Mysql了解嗎,里面有哪些鎖?
- 類型分類:共享鎖(S),獨占鎖(X),意向鎖(與表鎖互斥)
- 粒度分類:行鎖、表鎖
38、行鎖怎么實現(xiàn)的?
- 不知道,這個時候已經(jīng)有點崩潰了,怎么這么多不知道nnd
39、講一下事務(wù)隔離級別吧
- RU、RC、RR、Serializable
40、你們用的是哪個隔離級別
- mysql默認的是RR,我們改成RC了
41、在默認隔離級別下會產(chǎn)生幻讀問題嗎?
- 會,這是幻讀是RR的經(jīng)典問題之一
42、描述一下幻讀
- 在T1里Select * From table where id = 1;若不存在該記錄則insert id = 1的記錄進去,但是在select完畢之后T2事務(wù)插入了id=1的record,此時后續(xù)insert執(zhí)行失敗,本質(zhì)上來講是當前的快照都不支持后續(xù)dml語句的執(zhí)行
43、MVCC機制了解嗎?
- 了解,由undolog支撐的數(shù)據(jù)隔離機制,主要是為了提供更高的并發(fā)度
44、講一下原理
- 每一行record都存在兩個隱藏行,一個是當前的事務(wù)id,一個是指向undolog的指針 mvcc機制運行
- 在rr和rc兩個隔離級別下 在每次生成ReadView的時候,會將當前的活躍事務(wù)ID維護在列表當中,如果訪問的Record的ID比最小活躍事務(wù)的ID還要小說明之前已經(jīng)提交了,可以直接讀取,如果與最大事務(wù)ID還要大就證明該事務(wù)在這個快照時沒提交,需要根據(jù)undolog去找對應(yīng)的歷史版本,如果在最大和最小之間,那么若其為活躍事務(wù)則找歷史版本,若不是則直接讀取
- 在RC級別下,每次Select都生成新的ReadView,所以能看到不同事物間的提交
- 在RR級別下,只在第一次Select的時候生成ReadView,所以會產(chǎn)生幻讀,因為快照讀和真實讀的結(jié)果不一致
45、慢sql怎么處理?
- 撈慢sql日志先分析寫的索引是不是有問題或者offset太大了,然后看expain
46、你關(guān)注explain的那些col?
- key:真實用到的索引
- possible_key:可能用的索引
- rows:掃描行數(shù),越大越拉垮
- filter:過濾數(shù)據(jù)比例,這個col可以驗證索引有效性
- extra:包含是否使用索引、sort是否時filesort等
47、https了解嗎?
- client發(fā)一個隨機數(shù)給server
- server發(fā)證書+隨機數(shù)回來
- client拆證書找第三方驗證證書有效性,取出公鑰
- client拿公鑰加密第三個隨機數(shù)發(fā)server
- server私鑰解密
48、線上機器cpu100%你怎么處理?
- 容器化時代,一定要top看下是不是st過高,存在超賣的可能性
- 如果不是的話top看下哪個進程有問題,然后看這個進程哪個線程吃了cpu
- jstack直接把線程dump出來然后找對應(yīng)有問題的線程再分析
- 也有可能是內(nèi)存泄漏導(dǎo)致的頻繁GC問題,可以拉GClog然后在jmap把heap dump出來看下
49、你們線上JVM一般調(diào)整什么參數(shù)?
- XMX&XMS固定防止內(nèi)存抖動
- 堆空間調(diào)整:年輕代Age調(diào)整、年輕代eden:s0:s1比例調(diào)整
- 收集器調(diào)整:大促前把CMS的預(yù)清理次數(shù)調(diào)低一些,CMS的清理閾值調(diào)高一些
50、反問
- 什么團隊?
- 做什么業(yè)務(wù)的?
自我反思

雖說這次是抱著試水的心態(tài)去的,但是這一連50問著實是有點傻眼了,而且也發(fā)現(xiàn)了自己的很多漏洞,如下:
- 我的簡歷過長,難以被面試官抓住重點
- 項目使用技術(shù)棧沒有體現(xiàn)出來
- 涉及相關(guān)項目重難點表述不是很清楚,分布式鎖、多租戶的分庫分表以及中間件隔離方案、性能問題排查等
- 各類技術(shù)棧停其實都還留在使用層,沒有深入去挖掘
- 語速太快了,70分鐘的面試大大小小回答了50個問題,我感覺放慢點夠我回答兩輪了
最后總結(jié)個人所得(供大家參考學(xué)習(xí))
這次一面結(jié)束之后我反思很久,發(fā)現(xiàn)自己真的是有很多不足和漏洞,所以最近一直在規(guī)劃自己的學(xué)習(xí)路線去不足,不論你是復(fù)習(xí)備戰(zhàn)面試還是自己學(xué)習(xí),我相信我所說的多少還是有點用處的。
1.1 首先,第一個應(yīng)該去梳理整個體系的知識大綱
整個體系的知識大綱
我將整個體系分為5個專題:并發(fā)編程、性能調(diào)優(yōu)、Spring全家桶、緩存數(shù)據(jù)庫、分布式&微服務(wù)
1.2 其次,根據(jù)上面的分類,按照大綱來學(xué)習(xí)(最后看面試專題)
對于每一個專題,去搜集相應(yīng)的面試學(xué)習(xí)筆記,比如下面我所收集的(若是對我收集的這份知識體系大綱以及下方每個專題對應(yīng)的面試+學(xué)習(xí)筆記感興趣,點擊傳送門 即可?。?/strong>
1. 并發(fā)編程(手寫筆記:并發(fā)編程+并發(fā)編程原理+并發(fā)編程應(yīng)用+并發(fā)編程_模式)
- 并發(fā)編程
并發(fā)編程
- 并發(fā)編程_原理
并發(fā)編程_原理
- 并發(fā)編程_應(yīng)用
并發(fā)編程_應(yīng)用
- 并發(fā)編程_模式
并發(fā)編程_模式
- 性能調(diào)優(yōu)(Java性能調(diào)優(yōu)實戰(zhàn):Java編程性能調(diào)優(yōu)+多線程性能調(diào)優(yōu)+JVM性能監(jiān)測及調(diào)優(yōu)+設(shè)計模式調(diào)優(yōu)+數(shù)據(jù)庫性能調(diào)優(yōu)+實戰(zhàn)演練)
性能調(diào)優(yōu)
- Spring全家桶(關(guān)注這一部分,我將Spring、MVC、Cloud、Boot歸整在一塊了)
- 手繪的各思維腦圖(幫助梳理知識點,比較多就不一一截圖了)
Spring全家桶手繪的各思維腦圖
- 進階學(xué)習(xí)的筆記
Spring全家桶進階學(xué)習(xí)的筆記
- 緩存數(shù)據(jù)庫(主要是MySQL+Redis+MongDB)
MySQL+Redis+MongDB
- 分布式&微服務(wù)(整理的筆記如下)
分布式&微服務(wù)
1.3 最后來看面試專題
我從基礎(chǔ)-中級-高級開始一步一步逐步深入,這些面試問題一樣都有分類整理,添加小助理vx:mxzFAFAFA即可獲取**
- 比如基礎(chǔ)部分:
基礎(chǔ)部分
- 中級部分:
中級部分
- 高級部分(消息隊列+Redis緩存+分庫分表+讀寫分離+分布式系統(tǒng)+高可用+微服務(wù)架構(gòu))
高級部分
以上就是我全部的一個學(xué)習(xí)路線的規(guī)劃了,從整體的一個知識體系出發(fā),梳理全部的知識,有漏洞就去查閱我相關(guān)的手寫筆記加以鞏固,最后上面試刷題,爭取查漏補缺,下次面試不再出現(xiàn)這么多的不知道和知識空白。
話說到這里,不論是知識體系大綱,還是相關(guān)的并發(fā)編程、性能調(diào)優(yōu)、Spring全家桶、緩存數(shù)據(jù)庫、分布式&微服務(wù)等等的筆記,如何你也想學(xué)習(xí)或者復(fù)習(xí)一下,那便可直接來找小編分享就行。
**只是麻煩大家?guī)兔D(zhuǎn)發(fā)一下(可以幫助更多有需要的人看見),然后添加小助理vx:mxzFAFAFA即可下載全部我的學(xué)習(xí)+復(fù)習(xí)+面試筆記的方式,我們一起學(xué)習(xí),加油!**
