前言
文字已收錄至我的GitHub倉庫,歡迎Star:github.com/bin39232820…
種一棵樹最好的時間是十年前,其次是現在
我知道很多人不玩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站學資料結構和演算法,雖然不是科班出身,但是學習是沒有邊界的。最後希望大家一起努力吧。感謝這個小夥伴的面試題分享。努力的路上你並不孤單,加油!
下面是我的公眾號,目前是啥也沒有?以後會通過公眾號給大家一些學習資源啥的,然後就是寫文章。嘗試著去運營一下。
日常求贊
好了各位,以上就是這篇文章的全部內容了,能看到這裡的人呀,都是真粉。
創作不易,各位的支援和認可,就是我創作的最大動力,我們下篇文章見
六脈神劍 | 文 【原創】如果本篇部落格有任何錯誤,請批評指教,不勝感激 !