【備戰春招/秋招系列】美團Java面經總結進階篇 (附詳解答案)

【備戰春招/秋招系列】美團面經總結進階篇 (附詳解答案)

該文已加入開源文檔:JavaGuide(一份涵蓋大部分Java程序員所須要掌握的核心知識)。地址:github.com/Snailclimb/….java

系列文章:git

這是我總結的美團面經的進階篇,後面還有終結篇哦!下面只是我從不少份美團面經中總結的在美團面試中一些常見的問題。不一樣於我的面經,這份面經具備普適性。每次面試必備的自我介紹、項目介紹這些東西,你們能夠本身私下好好思考。我在前面的文章中也提到了應該怎麼作自我介紹與項目介紹,詳情能夠查看這篇文章:【備戰春招/秋招系列2】初出茅廬的程序員該如何準備面試?程序員

有人私信我讓我對美團面試難度作一個評級,我以爲若是有10級的話,美團面試的難度大概在6級左右吧!部分狀況可能因人而異了。github

消息隊列/消息中間件應該是Java程序員必備的一個技能了,若是你以前沒接觸過消息隊列的話,建議先去百度一下某某消息隊列入門,而後花2個小時就差很少能夠學會任何一種消息隊列的使用了。若是說僅僅學會使用是萬萬不夠的,在實際生產環境還要考慮消息丟失等等狀況。關於消息隊列面試相關的問題,推薦你們也能夠看一下視頻《Java工程師面試突擊第1季-中華石杉老師》,若是你們沒有資源的話,能夠在個人公衆號「Java面試通關手冊」後臺回覆關鍵字「1」便可!面試

一 消息隊列MQ的套路

面試官通常會先問你這個問題,預熱一下,看你知道消息隊列不,通常在第一面的時候面試官可能只會問消息隊列MQ的應用場景/使用消息隊列的好處、使用消息隊列會帶來什麼問題、消息隊列的技術選型這幾個問題,不會太深究下去,在後面的第二輪/第三輪技術面試中可能會深刻問一下。數據庫

1.1 介紹一下消息隊列MQ的應用場景/使用消息隊列的好處

《大型網站技術架構》第四章和第七章均有提到消息隊列對應用性能及擴展性的提高。數組

①.經過異步處理提升系統性能

經過異步處理提升系統性能
如上圖, 在不使用消息隊列服務器的時候,用戶的請求數據直接寫入數據庫,在高併發的狀況下數據庫壓力劇增,使得響應速度變慢。可是在使用消息隊列以後,用戶的請求數據發送給消息隊列以後當即 返回,再由消息隊列的消費者進程從消息隊列中獲取數據,異步寫入數據庫。因爲消息隊列服務器處理速度快於數據庫(消息隊列也比數據庫有更好的伸縮性),所以響應速度獲得大幅改善。

經過以上分析咱們能夠得出消息隊列具備很好的削峯做用的功能——即經過異步處理,將短期高併發產生的事務消息存儲在消息隊列中,從而削平高峯期的併發事務。 舉例:在電子商務一些秒殺、促銷活動中,合理使用消息隊列能夠有效抵禦促銷活動剛開始大量訂單涌入對系統的衝擊。以下圖所示: 緩存

合理使用消息隊列能夠有效抵禦促銷活動剛開始大量訂單涌入對系統的衝擊
由於 用戶請求數據寫入消息隊列以後就當即返回給用戶了,可是請求數據在後續的業務校驗、寫數據庫等操做中可能失敗。所以使用消息隊列進行異步處理以後,須要 適當修改業務流程進行配合,好比 用戶在提交訂單以後,訂單數據寫入消息隊列,不能當即返回用戶訂單提交成功,須要在消息隊列的訂單消費者進程真正處理完該訂單以後,甚至出庫後,再經過電子郵件或短信通知用戶訂單成功,以避免交易糾紛。這就相似咱們平時手機訂火車票和電影票。

②.下降系統耦合性

咱們知道模塊分佈式部署之後聚合方式一般有兩種:1.分佈式消息隊列和2.分佈式服務安全

先來簡單說一下分佈式服務:服務器

目前使用比較多的用來構建SOA(Service Oriented Architecture面向服務體系結構)分佈式服務框架是阿里巴巴開源的Dubbo.若是想深刻了解Dubbo的能夠看我寫的關於Dubbo的這一篇文章:《高性能優秀的服務框架-dubbo介紹》juejin.im/post/5acade…

再來談咱們的分佈式消息隊列:

咱們知道若是模塊之間不存在直接調用,那麼新增模塊或者修改模塊就對其餘模塊影響較小,這樣系統的可擴展性無疑更好一些。

咱們最多見的事件驅動架構相似生產者消費者模式,在大型網站中一般用利用消息隊列實現事件驅動結構。以下圖所示:

利用消息隊列實現事件驅動結構
消息隊列使利用發佈-訂閱模式工做,消息發送者(生產者)發佈消息,一個或多個消息接受者(消費者)訂閱消息。 從上圖能夠看到 消息發送者(生產者)和消息接受者(消費者)之間沒有直接耦合,消息發送者將消息發送至分佈式消息隊列即結束對消息的處理,消息接受者從分佈式消息隊列獲取該消息後進行後續處理,並不須要知道該消息從何而來。 對新增業務,只要對該類消息感興趣,便可訂閱該消息,對原有系統和業務沒有任何影響,從而實現網站業務的可擴展性設計

消息接受者對消息進行過濾、處理、包裝後,構形成一個新的消息類型,將消息繼續發送出去,等待其餘消息接受者訂閱該消息。所以基於事件(消息對象)驅動的業務架構能夠是一系列流程。

另外爲了不消息隊列服務器宕機形成消息丟失,會將成功發送到消息隊列的消息存儲在消息生產者服務器上,等消息真正被消費者服務器處理後才刪除消息。在消息隊列服務器宕機後,生產者服務器會選擇分佈式消息隊列服務器集羣中的其餘服務器發佈消息。

備註: 不要認爲消息隊列只能利用發佈-訂閱模式工做,只不過在解耦這個特定業務環境下是使用發佈-訂閱模式的,好比在咱們的ActiveMQ消息隊列中還有點對點工做模式,具體的會在後面的文章給你們詳細介紹,這一篇文章主要仍是讓你們對消息隊列有一個更透徹的瞭解。

這個問題通常會在上一個問題問完以後,緊接着被問到。「使用消息隊列會帶來什麼問題?」這個問題要引發重視,通常咱們都會考慮使用消息隊列會帶來的好處而忽略它帶來的問題!

1.2 那麼使用消息隊列會帶來什麼問題?考慮過這個問題嗎?

  • **系統可用性下降:**系統可用性在某種程度上下降,爲何這樣說呢?在加入MQ以前,你不用考慮消息丟失或者說MQ掛掉等等的狀況,可是,引入MQ以後你就須要去考慮了!
  • 系統複雜性提升: 加入MQ以後,你須要保證消息沒有被重複消費、處理消息丟失的狀況、保證消息傳遞的順序性等等問題!
  • 一致性問題: 我上面講了消息隊列能夠實現異步,消息隊列帶來的異步確實能夠提升系統響應速度。可是,萬一消息的真正消費者並無正確消費消息怎麼辦?這樣就會致使數據不一致的狀況了!

瞭解下面這個問題是爲了咱們更好的進行技術選型!該部分摘自:《Java工程師面試突擊第1季-中華石杉老師》,若是你們沒有資源的話,能夠在個人公衆號「Java面試通關手冊」後臺回覆關鍵字「1」便可!

1.3 介紹一下你知道哪幾種消息隊列,該如何選擇呢?

特性 ActiveMQ RabbitMQ RocketMQ Kafaka
單機吞吐量 萬級,吞吐量比RocketMQ和Kafka要低了一個數量級 萬級,吞吐量比RocketMQ和Kafka要低了一個數量級 10萬級,RocketMQ也是能夠支撐高吞吐的一種MQ 10萬級別,這是kafka最大的優勢,就是吞吐量高。通常配合大數據類的系統來進行實時數據計算、日誌採集等場景
topic數量對吞吐量的影響 topic能夠達到幾百,幾千個的級別,吞吐量會有較小幅度的降低這是RocketMQ的一大優點,在同等機器下,能夠支撐大量的topic topic從幾十個到幾百個的時候,吞吐量會大幅度降低。因此在同等機器下,kafka儘可能保證topic數量不要過多。若是要支撐大規模topic,須要增長更多的機器資源
可用性 高,基於主從架構實現高可用性 高,基於主從架構實現高可用性 很是高,分佈式架構 很是高,kafka是分佈式的,一個數據多個副本,少數機器宕機,不會丟失數據,不會致使不可用
消息可靠性 有較低的機率丟失數據 通過參數優化配置,能夠作到0丟失 通過參數優化配置,消息能夠作到0丟失
時效性 ms級 微秒級,這是rabbitmq的一大特色,延遲是最低的 ms級 延遲在ms級之內
功能支持 MQ領域的功能極其完備 基於erlang開發,因此併發能力很強,性能極其好,延時很低 MQ功能較爲完善,仍是分佈式的,擴展性好 功能較爲簡單,主要支持簡單的MQ功能,在大數據領域的實時計算以及日誌採集被大規模使用,是事實上的標準
優劣勢總結 很是成熟,功能強大,在業內大量的公司以及項目中都有應用。偶爾會有較低機率丟失消息,並且如今社區以及國內應用都愈來愈少,官方社區如今對ActiveMQ 5.x維護愈來愈少,幾個月才發佈一個版本並且確實主要是基於解耦和異步來用的,較少在大規模吞吐的場景中使用 erlang語言開發,性能極其好,延時很低;吞吐量到萬級,MQ功能比較完備並且開源提供的管理界面很是棒,用起來很好用。社區相對比較活躍,幾乎每月都發布幾個版本分在國內一些互聯網公司近幾年用rabbitmq也比較多一些可是問題也是顯而易見的,RabbitMQ確實吞吐量會低一些,這是由於他作的實現機制比較重。並且erlang開發,國內有幾個公司有實力作erlang源碼級別的研究和定製?若是說你沒這個實力的話,確實偶爾會有一些問題,你很難去看懂源碼,你公司對這個東西的掌控很弱,基本職能依賴於開源社區的快速維護和修復bug。並且rabbitmq集羣動態擴展會很麻煩,不過這個我以爲還好。其實主要是erlang語言自己帶來的問題。很難讀源碼,很難定製和掌控。 接口簡單易用,並且畢竟在阿里大規模應用過,有阿里品牌保障。日處理消息上百億之多,能夠作到大規模吞吐,性能也很是好,分佈式擴展也很方便,社區維護還能夠,可靠性和可用性都是ok的,還能夠支撐大規模的topic數量,支持複雜MQ業務場景。並且一個很大的優點在於,阿里出品都是java系的,咱們能夠本身閱讀源碼,定製本身公司的MQ,能夠掌控。社區活躍度相對較爲通常,不過也還能夠,文檔相對來講簡單一些,而後接口這塊不是按照標準JMS規範走的有些系統要遷移須要修改大量代碼。還有就是阿里出臺的技術,你得作好這個技術萬一被拋棄,社區黃掉的風險,那若是大家公司有技術實力我以爲用RocketMQ挺好的 kafka的特色其實很明顯,就是僅僅提供較少的核心功能,可是提供超高的吞吐量,ms級的延遲,極高的可用性以及可靠性,並且分佈式能夠任意擴展。同時kafka最好是支撐較少的topic數量便可,保證其超高吞吐量。並且kafka惟一的一點劣勢是有可能消息重複消費,那麼對數據準確性會形成極其輕微的影響,在大數據領域中以及日誌採集中,這點輕微影響能夠忽略這個特性自然適合大數據實時計算以及日誌收集。

這部份內容,我這裏不給出答案,你們能夠自行根據本身學習的消息隊列查閱相關內容,我可能會在後面的文章中介紹到這部份內容。另外,下面這些問題在視頻《Java工程師面試突擊第1季-中華石杉老師》中都有提到,若是你們沒有資源的話,能夠在個人公衆號「Java面試通關手冊」後臺回覆關鍵字「1」便可!

1.4 關於消息隊列其餘一些常見的問題展望

  1. 引入消息隊列以後如何保證高可用性
  2. 如何保證消息不被重複消費呢?
  3. 如何保證消息的可靠性傳輸(如何處理消息丟失的問題)?
  4. 我該怎麼保證從消息隊列裏拿到的數據按順序執行?
  5. 如何解決消息隊列的延時以及過時失效問題?消息隊列滿了之後該怎麼處理?有幾百萬消息持續積壓幾小時,說說怎麼解決?
  6. 若是讓你來開發一個消息隊列中間件,你會怎麼設計架構?

二 談談 InnoDB 和 MyIsam 二者的區別

2.1 二者的對比

  1. count運算上的區別: 由於MyISAM緩存有表meta-data(行數等),所以在作COUNT(*)時對於一個結構很好的查詢是不須要消耗多少資源的。而對於InnoDB來講,則沒有這種緩存。

  2. 是否支持事務和崩潰後的安全恢復: MyISAM 強調的是性能,每次查詢具備原子性,其執行數度比InnoDB類型更快,可是不提供事務支持。可是InnoDB 提供事務支持事務,外部鍵等高級數據庫功能。 具備事務(commit)、回滾(rollback)和崩潰修復能力(crash recovery capabilities)的事務安全(transaction-safe (ACID compliant))型表。

3)是否支持外鍵: MyISAM不支持,而InnoDB支持。

2.2 關於二者的總結

MyISAM更適合讀密集的表,而InnoDB更適合寫密集的的表。 在數據庫作主從分離的狀況下,常常選擇MyISAM做爲主庫的存儲引擎。

通常來講,若是須要事務支持,而且有較高的併發讀取頻率(MyISAM的表鎖的粒度太大,因此當該表寫併發量較高時,要等待的查詢就會不少了),InnoDB是不錯的選擇。若是你的數據量很大(MyISAM支持壓縮特性能夠減小磁盤的空間佔用),並且不須要支持事務時,MyISAM是最好的選擇。

三 聊聊 Java 中的集合吧!

3.1 Arraylist 與 LinkedList 有什麼不一樣?(注意加上從數據結構分析的內容)

  • 1. 是否保證線程安全: ArrayList 和 LinkedList 都是不一樣步的,也就是不保證線程安全;
  • 2. 底層數據結構: Arraylist 底層使用的是Object數組;LinkedList 底層使用的是雙向鏈表數據結構(注意雙向鏈表和雙向循環鏈表的區別:);
  • 3. 插入和刪除是否受元素位置的影響:ArrayList 採用數組存儲,因此插入和刪除元素的時間複雜度受元素位置的影響。 好比:執行add(E e)方法的時候, ArrayList 會默認在將指定的元素追加到此列表的末尾,這種狀況時間複雜度就是O(1)。可是若是要在指定位置 i 插入和刪除元素的話(add(int index, E element))時間複雜度就爲 O(n-i)。由於在進行上述操做的時候集合中第 i 和第 i 個元素以後的(n-i)個元素都要執行向後位/向前移一位的操做。 ② LinkedList 採用鏈表存儲,因此插入,刪除元素時間複雜度不受元素位置的影響,都是近似 O(1)而數組爲近似 O(n)。
  • 4. 是否支持快速隨機訪問: LinkedList 不支持高效的隨機元素訪問,而 ArrayList 支持。快速隨機訪問就是經過元素的序號快速獲取元素對象(對應於get(int index)方法)。
  • 5. 內存空間佔用: ArrayList的空 間浪費主要體如今在list列表的結尾會預留必定的容量空間,而LinkedList的空間花費則體如今它的每個元素都須要消耗比ArrayList更多的空間(由於要存放直接後繼和直接前驅以及數據)。

補充內容:RandomAccess接口

public interface RandomAccess {
}
複製代碼

查看源碼咱們發現實際上 RandomAccess 接口中什麼都沒有定義。因此,在我看來 RandomAccess 接口不過是一個標識罷了。標識什麼? 標識實現這個接口的類具備隨機訪問功能。

在binarySearch()方法中,它要判斷傳入的list 是否RamdomAccess的實例,若是是,調用indexedBinarySearch()方法,若是不是,那麼調用iteratorBinarySearch()方法

public static <T>
    int binarySearch(List<? extends Comparable<? super T>> list, T key) {
        if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
            return Collections.indexedBinarySearch(list, key);
        else
            return Collections.iteratorBinarySearch(list, key);
    }

複製代碼

ArraysList 實現了 RandomAccess 接口, 而 LinkedList 沒有實現。爲何呢?我以爲仍是和底層數據結構有關!ArraysList 底層是數組,而 LinkedList 底層是鏈表。數組自然支持隨機訪問,時間複雜度爲 O(1),因此稱爲快速隨機訪問。鏈表須要遍歷到特定位置才能訪問特定位置的元素,時間複雜度爲 O(n),因此不支持快速隨機訪問。,ArraysList 實現了 RandomAccess 接口,就代表了他具備快速隨機訪問功能。 RandomAccess 接口只是標識,並非說 ArraysList 實現 RandomAccess 接口才具備快速隨機訪問功能的!

下面再總結一下 list 的遍歷方式選擇:

  • 實現了RadmoAcces接口的list,優先選擇普通for循環 ,其次foreach,
  • 未實現RadmoAcces接口的ist, 優先選擇iterator遍歷(foreach遍歷底層也是經過iterator實現的),大size的數據,千萬不要使用普通for循環

Java 中的集合這類問題幾乎是面試必問的,問到這類問題的時候,HashMap 又是幾乎必問的問題,因此你們必定要引發重視!

3.2 HashMap的底層實現

####① JDK1.8以前

JDK1.8 以前 HashMap 底層是 數組和鏈表 結合在一塊兒使用也就是 鏈表散列HashMap 經過 key 的 hashCode 通過擾動函數處理事後獲得 hash 值,而後經過 (n - 1) & hash 判斷當前元素存放的位置(這裏的 n 指的時數組的長度),若是當前位置存在元素的話,就判斷該元素與要存入的元素的 hash 值以及 key 是否相同,若是相同的話,直接覆蓋,不相同就經過拉鍊法解決衝突。

所謂擾動函數指的就是 HashMap 的 hash 方法。使用 hash 方法也就是擾動函數是爲了防止一些實現比較差的 hashCode() 方法 換句話說使用擾動函數以後能夠減小碰撞。

JDK 1.8 HashMap 的 hash 方法源碼:

JDK 1.8 的 hash方法 相比於 JDK 1.7 hash 方法更加簡化,可是原理不變。

static final int hash(Object key) {
        int h;
        // key.hashCode():返回散列值也就是hashcode
        // ^ :按位異或
        // >>>:無符號右移,忽略符號位,空位都以0補齊
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
複製代碼

對比一下 JDK1.7的 HashMap 的 hash 方法源碼.

static int hash(int h) {
    // This function ensures that hashCodes that differ only by
    // constant multiples at each bit position have a bounded
    // number of collisions (approximately 8 at default load factor).

    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}
複製代碼

相比於 JDK1.8 的 hash 方法 ,JDK 1.7 的 hash 方法的性能會稍差一點點,由於畢竟擾動了 4 次。

所謂 「拉鍊法」 就是:將鏈表和數組相結合。也就是說建立一個鏈表數組,數組中每一格就是一個鏈表。若遇到哈希衝突,則將衝突的值加到鏈表中便可。

jdk1.8以前的內部結構

###② JDK1.8以後

相比於以前的版本, JDK1.8以後在解決哈希衝突時有了較大的變化,當鏈表長度大於閾值(默認爲8)時,將鏈表轉化爲紅黑樹,以減小搜索時間。

JDK1.8以後的HashMap底層數據結構

TreeMap、TreeSet以及JDK1.8以後的HashMap底層都用到了紅黑樹。紅黑樹就是爲了解決二叉查找樹的缺陷,由於二叉查找樹在某些狀況下會退化成一個線性結構。

問完 HashMap 的底層原理以後,面試官可能就會緊接着問你 HashMap 底層數據結構相關的問題!

3.3 既然談到了紅黑樹,你給我手繪一個出來吧,而後簡單講一下本身對於紅黑樹的理解

紅黑樹

紅黑樹特色:

  1. 每一個節點非紅即黑;
  2. 根節點老是黑色的;
  3. 每一個葉子節點都是黑色的空節點(NIL節點);
  4. 若是節點是紅色的,則它的子節點必須是黑色的(反之不必定);
  5. 從根節點到葉節點或空子節點的每條路徑,必須包含相同數目的黑色節點(即相同的黑色高度)

紅黑樹的應用:

TreeMap、TreeSet以及JDK1.8以後的HashMap底層都用到了紅黑樹。

爲何要用紅黑樹

簡單來講紅黑樹就是爲了解決二叉查找樹的缺陷,由於二叉查找樹在某些狀況下會退化成一個線性結構。

3.4 紅黑樹這麼優秀,爲什麼不直接使用紅黑樹得了?

說一下本身對於這個問題的見解:咱們知道紅黑樹屬於(自)平衡二叉樹,可是爲了保持「平衡」是須要付出代價的,紅黑樹在插入新數據後可能須要經過左旋,右旋、變色這些操做來保持平衡,這費事啊。你說說咱們引入紅黑樹就是爲了查找數據快,若是鏈表長度很短的話,根本不須要引入紅黑樹的,你引入以後還要付出代價維持它的平衡。可是鏈表過長就不同了。至於爲何選 8 這個值呢?經過幾率統計所得,這個值是綜合查詢成本和新增元素成本得出的最好的一個值。

3.5 HashMap 和 Hashtable 的區別/HashSet 和 HashMap 區別

HashMap 和 Hashtable 的區別

  1. 線程是否安全: HashMap 是非線程安全的,HashTable 是線程安全的;HashTable 內部的方法基本都通過 synchronized 修飾。(若是你要保證線程安全的話就使用 ConcurrentHashMap 吧!);
  2. 效率: 由於線程安全的問題,HashMap 要比 HashTable 效率高一點。另外,HashTable 基本被淘汰,不要在代碼中使用它;
  3. 對Null key 和Null value的支持: HashMap 中,null 能夠做爲鍵,這樣的鍵只有一個,能夠有一個或多個鍵所對應的值爲 null。。可是在 HashTable 中 put 進的鍵值只要有一個 null,直接拋出 NullPointerException。
  4. 初始容量大小和每次擴充容量大小的不一樣 : ①建立時若是不指定容量初始值,Hashtable 默認的初始大小爲11,以後每次擴充,容量變爲原來的2n+1。HashMap 默認的初始化大小爲16。以後每次擴充,容量變爲原來的2倍。②建立時若是給定了容量初始值,那麼 Hashtable 會直接使用你給定的大小,而 HashMap 會將其擴充爲2的冪次方大小(HashMap 中的tableSizeFor()方法保證,下面給出了源代碼)。也就是說 HashMap 老是使用2的冪做爲哈希表的大小,後面會介紹到爲何是2的冪次方。
  5. 底層數據結構: JDK1.8 之後的 HashMap 在解決哈希衝突時有了較大的變化,當鏈表長度大於閾值(默認爲8)時,將鏈表轉化爲紅黑樹,以減小搜索時間。Hashtable 沒有這樣的機制。

HashSet 和 HashMap 區別

若是你看過 HashSet 源碼的話就應該知道:HashSet 底層就是基於 HashMap 實現的。(HashSet 的源碼很是很是少,由於除了 clone() 方法、writeObject()方法、readObject()方法是 HashSet 本身不得不實現以外,其餘方法都是直接調用 HashMap 中的方法。)

HashSet 和 HashMap 區別

ThoughtWorks准入職Java工程師。專一Java知識分享!開源 Java 學習指南——JavaGuide(12k+ Star)的做者。公衆號多篇文章被各大技術社區轉載。公衆號後臺回覆關鍵字「1」能夠領取一份我精選的Java資源哦!

個人公衆號
相關文章
相關標籤/搜索