前言
文本已收錄至我的GitHub倉庫,歡迎Star:https://github.com/bin392328206/six-finger
種一棵樹最好的時間是十年前,其次是現在
我知道很多人不玩qq了,但是懷舊一下,歡迎加入六脈神劍Java菜鳥學習羣,羣聊號碼:549684836 鼓勵大家在技術的路上寫博客
絮叨
話說我上篇文章不是已經掛了嗎,怎麼又來水文章了,難道我是看上了掘金的獎勵了哈哈。。對我就是看上了,哈哈,下面是上一篇博客的地址
故事起因
一大早,羣裏一個老哥發私信給我說,它和我有一模一樣的面試經歷,有些東西想請教我,然後就聊上了,這個老哥是在我們老家衡陽讀的大學,頓時有點情切感,然後和它聊了很久,發現了一個很大的問題,同時渣渣二本,怎麼差距就那麼大,它的實力還是可以的。他已經過了3面了 我估計還有2面就能過了,同時也說明,我們這些渣渣本科,只要好好學習,也還是有機會的,然後我就藉此把它的面試題要過來了,然後我自己嘗試的去回答一下, 算是一個複習吧。
感謝寫文章,能讓我們在成長的路上共同進步,所以有想一起學習的,快來加羣吧,雖然沒有啥大佬,但是我覺得一起學習,比有大佬的羣 更重要,這就是一個氛圍吧。QQ羣聊號碼:549684836
老哥的一面面試題
- 線程的幾種狀態?哪些狀態有鎖。
- 說說線程池?
- 線程池的拒絕策略?
- 讓你自己設計一個線程池你如何設計?
- lock和synchronized區別?
- jdk對synchronized做了哪些優化,講講鎖升級過程?
- gc熟悉嗎?講一下CMS?
- CMS跟G1的區別?
- G1的設計有什麼特點(優勢)?
- 常見的類加載器有哪些?
- 什麼是雙親委派模型?
- 如何打破雙親委派模型?
- 你平常用的API中有哪些是打破了雙親委派的?
- 類加載的過程詳細說說?
- 講一下項目的基本架構?用到了哪些中間件?
- 講一下Redis擊穿,穿透,雪崩?
- Redis主從和哨兵說說看?
- 講一下常用rabbit,kafka,activemq,rocketmq的區別?
- kafka如何保證消息不丟失?不重複消費?
- 那kafka如何保證順序消費?
- 分佈式鎖有哪些實現方式?
- Redis分佈式鎖是怎麼實現的?
- 項目中碰到過死鎖嗎?產生死鎖的原因是什麼?
- 高併發情況如何對項目做優化?
- 項目中碰到過什麼難題?
- 你有什麼想問我的嗎?
我的回答
Tips 這個就是我自己還原一下場景,然後我就當我自己是在被面試,看看自己會怎麼去回答這些問題,看看自己的不足,其實讀者也可以去嘗試一下,算是一個查漏補缺吧
線程的幾種狀態?哪些狀態有鎖。
在Java線程中有以下幾種狀態,初始,運行中(就緒和運行) 阻塞(調用同步方法),等待,超時等待,和死亡這幾種狀態,在阻塞方法裏面是有鎖的,然後線程中的wait方法是會釋放鎖的,所以join方法也會釋放鎖,底層是wait,而sleep,yield是不會釋放鎖的
說說線程池?
線程池就是一個池技術,類似於我們的數據庫連接詞,可以減少線程創建的時間開銷。當有任務來的時候,可以直接對任務進行處理,提高響應速度。然後我再把線程池的運行過程補一下
首先一個任務調用execute方法的時候的大致流程
- 如果當前運行的線程少於corePoolSize,則創建一個corePoolSize去執行任務(創建的過程是加鎖的)
- 如果當前線程大於corePoolSize,那麼就把當前任務加入到一個阻塞隊列中去。
- 如果說當前阻塞隊列也滿了,則會創建一個新的線程來處理任務
- 如果判斷創建新的線程之後,線程池的數量>maxPoolSize,則把他交給拒絕策略去處理。
線程池的拒絕策略
當線程池的處理任務的能力達到飽和之後,我們可以選擇以下的幾種策略
- 默認的就是直接拋出異常
- 直接拋棄該任務
- 拋棄阻塞隊列中的第一個任務,把這個任務加入到隊列裏面
- 直接由提交任務的線程去執行任務,然後阻塞當前提交任務的線程。
讓你自己設計一個線程池你如何設計
首先讓我設計一個線程池,我會考慮我線上的硬件水平如果我是4核的話,那麼我線程池的核心線程 我的核心線程最多給4個 然後最大線程數給8個,如果是8核就 核心線程數給6個 最大線程數給12個。其實這個還是得看場景是cpu密集型 還是io密集。但是上面的方案其實是一個折中的方案
然後設置一下線程的存活時間給個3分鐘吧,他的拒絕策略,就用最後一種吧就是當滿了的時候,用提交的線程去執行任務。設置他的一個阻塞隊列LinkedBlockingQueue 基於鏈表的隊列 吞吐量稍微大點 Spring的線程池的實現默認就是他,然後設置一下任務的個數,設置個500差不多了。看實際業務,一般項目裏面的線程池我們就直接用Spring的實現就行了。
lock和synchronized區別
相同點,都是爲了線程安全,
不同點
- synchronized 是一個Java關鍵字,二lock是一個Java類,一個是JVM層面,一個代碼層面
- synchronized會自動釋放鎖,但是lock要手動釋放 在拋異常的時候。
- synchronized是非公平的,lock可以是公平的也可以是非公平的
- synchronized是悲觀鎖(鎖升級之後),而lock底層是cas 樂觀鎖的實現
jdk對synchronized做了哪些優化,講講鎖升級過程?
就是鎖升級的過程
當一個線程訪問一個含有synchronized的方法 代碼塊的時候,首先會去鎖住的這個對象的對象頭裏面的markword的鎖標記位從無鎖變成一個偏向鎖的過程,並且記錄當前的線程id,第二種情況當一個線程來訪問這個同步方法的時候,當前鎖對象的鎖標記位已經是偏向鎖,然後就對比線程的id,發現線程id並不匹配,那麼就會進行一個偏向鎖撤銷的過程,這個過程會暫停偏向鎖的線程(正好當前線程在執行的情況),如果當前線程還需要獲取鎖,則升級爲輕量級的鎖,通過cas來獲取鎖,並改變markword的標記位,如果一個線程的cas次數超過10次,則會升級成重量級鎖。進入一個阻塞隊列,是一個非公平的鎖。
gc熟悉嗎?講一下CMS?
還行,CMS是一種垃圾回收器一般配合新生代的ParNew來使用。它的目標是以最小的停頓時間爲目標的一個垃圾回收器,基於標記清除的算法實現
它的GC分爲4個階段,再GC的日誌中也是有體現的
- 初始標記階段,這個階段是用來標記GCRoot的,此過程會觸發Stop The World
- 併發標記,根據GCroot 來標記需要清除的對象
- 重新標記,這個過程也要Stop The World 因爲再併發標記的過程中,可能有新的垃圾對象進來了,所以需要再次檢驗一下。
- 併發清除,把前面標記的垃圾對象,清除掉。
CMS跟G1的區別
其實G1的回收機制和CMS很像,但是他們的區別就是region的概念,然後就是把內存分成了2048個分區,然後可以對部分的區進行回收,這樣回收的對象就會小很多,那麼每次Stop The World的時間就會少很多,所以對於大配置的機器用G1比較好,爲啥呢?如果我們cms 那麼等我們Full GC的時候,我們停頓的時間會很長,對於系統來說是有很大的影響的。
G1的設計有什麼特點
和上面差不多,可以由我們手動控制 Stop The World的時間,這點是非常牛逼了。G1收集器基本上不犧牲吞吐量的情況下完成低停頓的內存回收;G1將Java堆(新生代和老年代)劃分成多個大大小小的獨立區域, 然後進行區域回收
常見的類加載器有哪些
- 啓動類加載器(最頂層)
- 擴展類加載器
- 應用程序加載器
- 自定義加載器
什麼是雙親委派模型
總的來說 八個字,向上檢查,向下加載
如果一個類接受到類加載請求,他自己不會去加載這個請求,而是將這個類加載請求委派給父類加載器,這樣一層一層傳送,直到到達啓動類加載器(Bootstrap ClassLoader)。 只有當父類加載器無法加載這個請求時,子加載器纔會嘗試自己去加載。
如何打破雙親委派模型
使用線程上下文類加載器,可以在執行線程中,拋棄雙親委派加載鏈模式,使用線程上下文裏的類加載器加載類.
,
你平常用的API中有哪些是打破了雙親委派的
tomcat 因爲它一個tomcat裏面可以放多個wabApp,所以它需要打破
類加載的過程詳細說說
類的加載過程
- 加載就是把.Java文件 加載到JVM裏面變成.class文件
- 驗證 驗證文件是否合法
- 準備 給對象的基本數據類型賦值默認值,對引用類型分配內存空間
- 解析 將符合引用替換成真實的地址引用
- 初始化,直接初始化一個對象
- 使用 就是用的過程
- 卸載
講一下項目的基本架構?用到了哪些中間件?
我們項目是一個類似於2B2C的一個教育平臺對標網易雲課堂和騰訊課堂,然後纔有的是微服務架構,分佈式系統開發,把整個系統拆分成了大概10多個基礎服務 例如商品 訂單 資訊 用戶 sso 支付 直播 錄播 等等,和幾個公共服務註冊中心 分佈式配置中心 分佈式系統調度中心 等等,然後中間件 用的rabbitmq redis cannal,es,skywalking 等等。
講一下Redis擊穿,穿透,雪崩?
- 雪崩就是當大量的緩存失效,導致了大量的請求打到了我們的db導致db崩潰的現象 這種我們一般是加一些隨機的過期時間,避免大量的key同時失效。
- 擊穿 就是一個hot key 或者幾個 hot key 直接把redis打崩了,就是正面剛,幹掉了你,一般這種情況很少,因爲我們肯定會預估我們的系統流量的瓶頸,在流量進入redis之前最限流操作的,或者我們會給redis集羣,提高redis的吞吐量
- 穿透,這個是有可能的,就是當我們請求api的時候,有時候有一些非法的key 進入到了我們的邏輯層,然後跳過了redis,然後直接把流量直接打到了我們的db,這種解決方式也很簡單,可以加一個過濾器,當我們存這個數據到redis中的時候,把他的key 也存到bitmap中,這樣查的時候先查bitmap如果裏面有就讓他去redis中拿,不然就直接返回,把流量攔截。
Redis主從和哨兵說說看
主從的話就是主節點負責寫,其他節點負責讀,這樣來提高redis的吞吐,但是這樣也有單點故障問題,所以我們線上的環境最好用哨兵模式,哨兵可以監控redis 集羣節點,可以做崩潰恢復,當主節點掛了,他可以選舉新的主節點,來保證單點問題。
講一下常用rabbit,kafka,activemq,rocketmq的區別?
首先 activemq 不會在選擇了 社區的活躍,性能都不在考慮的範圍,對於技術選型的話,首先就幹掉它
- 吞吐來說 kafka 和rocketmq 的吞吐要比rabbit 高很多
- 時延 rabbit 最低的延時,但是後面都差不多
- 社區 rabbit活躍度不錯,rocketmq 阿里的 也不錯,kafka 也還行
- 可用性 rabbit 基於主從,其他2個基於分佈式系統,可用性比他高很多
- 其實除去這些對於技術的選型,要考慮團隊的熟悉度,還有你的場景,rocketmq 他的事務機制,對於很多場景是不錯,kafka對於日誌等場景是非常不錯的,rabbit對於小公司也是很不錯的,所以這個選型沒有說哪個最好,只有最合適。
kafka如何保證消息不丟失?不重複消費?
其實這種問題對於任何一個mq的回答都是通用的,我們公司用的rabbitmq 我就用rabbitmq來回答了
肯定是從三個地方來回答這種問題
- 發送端 保證發送端的消息一定是能夠發送到我們的中間件裏面,可以採取一個cobnfirm機制,如果失敗了,就重新發送,
- 在我們rabbitmq的時候,消息也可能丟失,這個時候,我們得做持久化。
- 消費端,我們要有ack機制,就是說必須說確定消費成功後,才能ack
對於重複消費,這種問題,我們就可以做冪等校驗,發送之前我們可以存一個唯一key,消費完之後刪除這個key,如果發現沒有這個key說明之前已經消費過了,這樣我們就可以做到防止重複消費。
那kafka如何保證順序消費
kafka的順序消息僅僅是通過partitionKey,將某類消息寫入同一個partition,一個partition只能對應一個消費線程,以保證數據有序。如果是rabbitmq其實也是一樣的,只是說我們的性能就沒有那麼好了。同一個隊列,對應一個線程去消費。
分佈式鎖有哪些實現方式,Redis分佈式鎖是怎麼實現的
對於分佈式鎖的實現方式 業屆一般有以下的實現方式
- 第一個在數據庫層面採用表鎖來實現,在數據庫中存方法名,並給他加上唯一索引,這種方式目前用的少,性能不怎麼行
- 第二個用zookeeper 臨時順序節點和他的watch機制來實現分佈式鎖,但是這種方案因爲我們公司沒有zk,所以沒有采用,用的是下面這種方案
- 通過redis 來實現分佈式鎖,我們知道一個分佈式鎖的四個條件就是 加鎖,解鎖,鎖超時,爲鎖續命,那麼我們的redis的setNX EX我們用lua把這個命令做成一個原子操作就可以實現一個分佈式鎖。但是考慮到業務的複雜性,我們公司用的是Redisson 來實現分佈式鎖,這邊我說說Redisson實現分佈式鎖的原理,它其實很簡單就是getLock 獲取鎖對象,然後調用tryLock方法,這個是一個重入的一個分佈式鎖的實現,參數可以是過期時間,和嘗試最大獲取鎖的時間,當我們調用這個方法的時候,首先是記錄進入的時候,然後判斷一下最大嘗試獲取鎖的時間,是不是達到最大,如果沒有達到最大,這個時候就去嘗試加鎖,這個時候如果已經有人加鎖了 就會返回過期時間,如果返回的時間爲0 這個就說明,此時這個時候是沒人加鎖的,這個時候,當前線程就可以獲取鎖,然後再次判斷一個最大嘗試獲取鎖的時間,超過了就直接返回獲取鎖失敗,接下來就是當前線程要去訂閱剛剛獲取鎖的那個線程的釋放鎖的事件,方便我這個線程下次去競爭鎖,然後進入到一個循環等待的過程,每次收到通知的時候,會判斷一下自己是不是超過最大時間了,如果超過了就直接返回鎖失敗,如果獲取鎖返回爲0時,去嘗試加鎖,當加鎖成功會返回null,不然就返回獲得鎖那個線程的過期時間。
項目中碰到過死鎖嗎?產生死鎖的原因是什麼?
這個我還沒真沒有碰到過
高併發情況如何對項目做優化
這個要回答的話,估計很長了,我說下自己的一些小思路吧,這個應該設計的系統架構的設計了,而且是高併發系統架構
- 首先前端 我們前端的 html 和css js 通過cdn 加載,這樣可以提供用戶加載的速度
- 然後是性能問題,正確估算性能的瓶頸,比如說做緩存前置,限流,儘可能的保證我們的應用程序一直可以用。
- 可以做一些流量的削鋒,異步的消費這些流量。
- 數據庫層面 讀寫分離 分庫分表,等等。或者用TiDB
- 然後服務拆分,儘量保證,一部分服務不影響你的整個項目
項目中碰到過什麼難題
系統重構,業務的代碼的優化,然後報表拆分,sql裏面全是業務,然後重構成代碼層面。遷移和聚合TB級的數據,然後保證數據的準確性。最後通過團隊的配合,然後努力 最後無縫遷移到新的架構,然後把BI功能成功從原來的耦合的業務性能拆分出來。保證我們的業務系統的性能提高很多,並且後面的迭代能夠更加輕鬆,對於系統的健壯性也提高不少。
你有什麼想問我的嗎
貴公司的技術棧,目前的項目類型。等等
結尾
上面是我自己一邊看題目一邊想的回答,估計真實面試場景的話,可能最好結合自己的項目來說吧,量化自己項目的指標可能會好點,比如多少的qps,然後TB級別的數據你是怎麼處理的,然後怎麼的,哈哈。我的回答大家看看就好,肯定也是很菜的,不過我寫這個就是想把這個當做一次面試經歷來寫的。上次掛在了算法上,最近在B站學數據結構和算法,雖然不是科班出身,但是學習是沒有邊界的。最後希望大家一起努力吧。感謝這個小夥伴的面試題分享。努力的路上你並不孤單,加油!
下面是我的公衆號,目前是啥也沒有?以後會通過公衆號給大家一些學習資源啥的,然後就是寫文章。嘗試着去運營一下。 回覆 888 海量的學習資料
日常求贊
好了各位,以上就是這篇文章的全部內容了,能看到這裏的人呀,都是真粉。
創作不易,各位的支持和認可,就是我創作的最大動力,我們下篇文章見
六脈神劍 | 文 【原創】如果本篇博客有任何錯誤,請批評指教,不勝感激 !