分享2019年螞蟻金服面經(已拿Offer)!附答案!!

EasonTyler發表於2019-05-12

由於作者面試過程中高度緊張,本文中只列出了自己還記得的部分題目。

經歷了漫長一個月的等待,終於在前幾天通過面試官獲悉已被螞蟻金服錄取,這期間的焦慮、痛苦自不必說,知道被錄取的那一刻,一整年的陰霾都一掃而空了。

筆者面的是阿里的Java研發工程師崗,面試流程是3輪技術面+1輪hr面。

1 意外的一面

一面的時候大概是3月12號,面完等了差不多半個月才突然接到二面面試官的電話。一面可能是簡歷面,所以問題比較簡單。

ArrayList和LinkedList區別

  • 1. 是否保證執行緒安全: ArrayList 和 LinkedList 都是不同步的,也就是不保證執行緒安全;

  • 2. 底層資料結構: Arraylist 底層使用的是Object陣列;LinkedList 底層使用的是雙向連結串列資料結構(JDK1.6之前為迴圈連結串列,JDK1.7取消了迴圈。注意雙向連結串列和雙向迴圈連結串列的區別,下面有介紹到!)

  • 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更多的空間(因為要存放直接後繼和直接前驅以及資料)。

什麼情況會造成記憶體洩漏

在Java中,記憶體洩漏就是存在一些被分配的物件,這些物件有下面兩個特點:

  1. 這些物件是可達的,即在有向圖中,存在通路可以與其相連;

  2. 這些物件是無用的,即程式以後不會再使用這些物件。

如果物件滿足這兩個條件,這些物件就可以判定為Java中的記憶體洩漏,這些物件不會被GC所回收,然而它卻佔用記憶體。

什麼是執行緒死鎖?如何解決?

認識執行緒死鎖

多個執行緒同時被阻塞,它們中的一個或者全部都在等待某個資源被釋放。由於執行緒被無限期地阻塞,因此程式不可能正常終止。

如下圖所示,執行緒 A 持有資源 2,執行緒 B 持有資源 1,他們同時都想申請對方的資源,所以這兩個執行緒就會互相等待而進入死鎖狀態。


分享2019年螞蟻金服面經(已拿Offer)!附答案!!
執行緒死鎖示意圖


下面通過一個例子來說明執行緒死鎖,程式碼模擬了上圖的死鎖的情況 (程式碼來源於《併發程式設計之美》):

public class DeadLockDemo {    private static Object resource1 = new Object();//資源 1    private static Object resource2 = new Object();//資源 2    public static void main(String[] args) {        new Thread(() -> {            synchronized (resource1) {                System.out.println(Thread.currentThread() + "get resource1");                try {                    Thread.sleep(1000);                } catch (InterruptedException e) {                    e.printStackTrace();                }                System.out.println(Thread.currentThread() + "waiting get resource2");                synchronized (resource2) {                    System.out.println(Thread.currentThread() + "get resource2");                }            }        }, "執行緒 1").start();        new Thread(() -> {            synchronized (resource2) {                System.out.println(Thread.currentThread() + "get resource2");                try {                    Thread.sleep(1000);                } catch (InterruptedException e) {                    e.printStackTrace();                }                System.out.println(Thread.currentThread() + "waiting get resource1");                synchronized (resource1) {                    System.out.println(Thread.currentThread() + "get resource1");                }            }        }, "執行緒 2").start();    }}複製程式碼

Output

Thread[執行緒 1,5,main]get resource1Thread[執行緒 2,5,main]get resource2Thread[執行緒 1,5,main]waiting get resource2Thread[執行緒 2,5,main]waiting get resource1複製程式碼

執行緒 A 通過 synchronized (resource1) 獲得 resource1 的監視器鎖,然後通過Thread.sleep(1000);讓執行緒 A 休眠 1s 為的是讓執行緒 B 得到執行然後獲取到 resource2 的監視器鎖。執行緒 A 和執行緒 B 休眠結束了都開始企圖請求獲取對方的資源,然後這兩個執行緒就會陷入互相等待的狀態,這也就產生了死鎖。上面的例子符合產生死鎖的四個必要條件。

學過作業系統的朋友都知道產生死鎖必須具備以下四個條件:

  1. 互斥條件:該資源任意一個時刻只由一個執行緒佔用。

  2. 請求與保持條件:一個程式因請求資源而阻塞時,對已獲得的資源保持不放。

  3. 不剝奪條件:執行緒已獲得的資源在末使用完之前不能被其他執行緒強行剝奪,只有自己使用完畢後才釋放資源。

  4. 迴圈等待條件:若干程式之間形成一種頭尾相接的迴圈等待資源關係。

如何避免執行緒死鎖?

我們只要破壞產生死鎖的四個條件中的其中一個就可以了。

破壞互斥條件

這個條件我們沒有辦法破壞,因為我們用鎖本來就是想讓他們互斥的(臨界資源需要互斥訪問)。

破壞請求與保持條件

一次性申請所有的資源。

破壞不剝奪條件

佔用部分資源的執行緒進一步申請其他資源時,如果申請不到,可以主動釋放它佔有的資源。

破壞迴圈等待條件

靠按序申請資源來預防。按某一順序申請資源,釋放資源則反序釋放。破壞迴圈等待條件。

我們對執行緒 2 的程式碼修改成下面這樣就不會產生死鎖了。

        new Thread(() -> {            synchronized (resource1) {                System.out.println(Thread.currentThread() + "get resource1");                try {                    Thread.sleep(1000);                } catch (InterruptedException e) {                    e.printStackTrace();                }                System.out.println(Thread.currentThread() + "waiting get resource2");                synchronized (resource2) {                    System.out.println(Thread.currentThread() + "get resource2");                }            }        }, "執行緒 2").start();複製程式碼

Output

Thread[執行緒 1,5,main]get resource1Thread[執行緒 1,5,main]waiting get resource2Thread[執行緒 1,5,main]get resource2Thread[執行緒 2,5,main]get resource1Thread[執行緒 2,5,main]waiting get resource2Thread[執行緒 2,5,main]get resource2Process finished with exit code 0複製程式碼

我們分析一下上面的程式碼為什麼避免了死鎖的發生?

執行緒 1 首先獲得到 resource1 的監視器鎖,這時候執行緒 2 就獲取不到了。然後執行緒 1 再去獲取 resource2 的監視器鎖,可以獲取到。然後執行緒 1 釋放了對 resource1、resource2 的監視器鎖的佔用,執行緒 2 獲取到就可以執行了。這樣就破壞了破壞迴圈等待條件,因此避免了死鎖。

紅黑樹是什麼?怎麼實現?時間複雜度?

紅黑樹(Red-Black Tree,簡稱R-B Tree),它一種特殊的二叉查詢樹。紅黑樹是特殊的二叉查詢樹,意味著它滿足二叉查詢樹的特徵:任意一個節點所包含的鍵值,大於等於左孩子的鍵值,小於等於右孩子的鍵值。除了具備該特性之外,紅黑樹還包括許多額外的資訊。

紅黑樹的每個節點上都有儲存位表示節點的顏色,顏色是紅(Red)或黑(Black)。紅黑樹的特性:

  1. 每個節點或者是黑色,或者是紅色。

  2. 根節點是黑色。

  3. 每個葉子節點是黑色。

  4. 如果一個節點是紅色的,則它的子節點必須是黑色的。

  5. 從一個節點到該節點的子孫節點的所有路徑上包含相同數目的黑節點。

關於它的特性,需要注意的是:

第一,特性(3)中的葉子節點,是隻為空(NIL或null)的節點。

第二,特性(5),確保沒有一條路徑會比其他路徑長出倆倍。因而,紅黑樹是相對是接近平衡的二叉樹。

分享2019年螞蟻金服面經(已拿Offer)!附答案!!
img

具體實現程式碼這裡不貼了,要實現起來,需要包含的基本操作是新增、刪除和旋轉。在對紅黑樹進行新增或刪除後,會用到旋轉方法。旋轉的目的是讓樹保持紅黑樹的特性。旋轉包括兩種:左旋 和 右旋。

紅黑樹的應用比較廣泛,主要是用它來儲存有序的資料,它的查詢、插入和刪除操作的時間複雜度是O(lgn)。

TCP 三次握手和四次揮手(面試常客)

為了準確無誤地把資料送達目標處,TCP協議採用了三次握手策略。

漫畫圖解:

圖片來源:《圖解HTTP》

分享2019年螞蟻金服面經(已拿Offer)!附答案!!
TCP三次握手


簡單示意圖:

分享2019年螞蟻金服面經(已拿Offer)!附答案!!
TCP三次握手


  • 客戶端–傳送帶有 SYN 標誌的資料包–一次握手–服務端

  • 服務端–傳送帶有 SYN/ACK 標誌的資料包–二次握手–客戶端

  • 客戶端–傳送帶有帶有 ACK 標誌的資料包–三次握手–服務端

為什麼要三次握手

三次握手的目的是建立可靠的通訊通道,說到通訊,簡單來說就是資料的傳送與接收,而三次握手最主要的目的就是雙方確認自己與對方的傳送與接收是正常的。

第一次握手:Client 什麼都不能確認;Server 確認了對方傳送正常

第二次握手:Client 確認了:自己傳送、接收正常,對方傳送、接收正常;Server 確認了:自己接收正常,對方傳送正常

第三次握手:Client 確認了:自己傳送、接收正常,對方傳送、接收正常;Server 確認了:自己傳送、接收正常,對方傳送、接收正常

所以三次握手就能確認雙發收發功能都正常,缺一不可。

為什麼要傳回 SYN

接收端傳回傳送端所傳送的 SYN 是為了告訴傳送端,我接收到的資訊確實就是你所傳送的訊號了。

SYN 是 TCP/IP 建立連線時使用的握手訊號。在客戶機和伺服器之間建立正常的 TCP 網路連線時,客戶機首先發出一個 SYN 訊息,伺服器使用 SYN-ACK 應答表示接收到了這個訊息,最後客戶機再以 ACK(Acknowledgement[漢譯:確認字元 ,在資料通訊傳輸中,接收站發給傳送站的一種傳輸控制字元。它表示確認發來的資料已經接受無誤。 ])訊息響應。這樣在客戶機和伺服器之間才能建立起可靠的TCP連線,資料才可以在客戶機和伺服器之間傳遞。

傳了 SYN,為啥還要傳 ACK

雙方通訊無誤必須是兩者互相傳送資訊都無誤。傳了 SYN,證明傳送方到接收方的通道沒有問題,但是接收方到傳送方的通道還需要 ACK 訊號來進行驗證。


分享2019年螞蟻金服面經(已拿Offer)!附答案!!
TCP四次揮手


斷開一個 TCP 連線則需要“四次揮手”:

  • 客戶端-傳送一個 FIN,用來關閉客戶端到伺服器的資料傳送

  • 伺服器-收到這個 FIN,它發回一 個 ACK,確認序號為收到的序號加1 。和 SYN 一樣,一個 FIN 將佔用一個序號

  • 伺服器-關閉與客戶端的連線,傳送一個FIN給客戶端

  • 客戶端-發回 ACK 報文確認,並將確認序號設定為收到序號加1

為什麼要四次揮手

任何一方都可以在資料傳送結束後發出連線釋放的通知,待對方確認後進入半關閉狀態。當另一方也沒有資料再傳送的時候,則發出連線釋放通知,對方確認後就完全關閉了TCP連線。

舉個例子:A 和 B 打電話,通話即將結束後,A 說“我沒啥要說的了”,B回答“我知道了”,但是 B 可能還會有要說的話,A 不能要求 B 跟著自己的節奏結束通話,於是 B 可能又巴拉巴拉說了一通,最後 B 說“我說完了”,A 回答“知道了”,這樣通話才算結束。

上面講的比較概括,推薦一篇講的比較細緻的文章:https://blog.csdn.net/qzcsu/article/details/72861891

突然的二面

一面的時候大概是3月12號,面完等了差不多半個月才突然接到二面面試官的電話。

介紹專案

Storm怎麼保證一致性

Storm是一個分散式的流處理系統,利用anchor和ack機制保證所有tuple都被成功處理。如果tuple出錯,則可以被重傳,但是如何保證出錯的tuple只被處理一次呢?Storm提供了一套事務性元件Transaction Topology,用來解決這個問題。

Transactional Topology目前已經不再維護,由Trident來實現事務性topology,但是原理相同。

參考:https://dwz.cn/8bXRPexB

說一下 HashMap 以及它是否執行緒安全

HashMap 基於雜湊表的 Map 介面的實現。HashMap 中,null 可以作為鍵,這樣的鍵只有一個;可以有一個或多個鍵所對應的值為null。HashMap 中 hash 陣列的預設大小是16,而且一定是2的指數。Hashtable、HashMap都使用了 Iterator。而由於歷史原因,Hashtable還使用了Enumeration的方式 。HashMap 實現 Iterator,支援fast-fail。

雜湊表是由陣列+連結串列組成的,它是通過把key值進行hash來定位物件的,這樣可以提供比線性儲存更好的效能。

分享2019年螞蟻金服面經(已拿Offer)!附答案!!
img

HashMap不是執行緒安全的。

十億條淘寶購買記錄,怎麼獲取出現最多的前十個

這是一道典型的有限記憶體的海量資料處理的題目。一般這類題目的解答無非是以下幾種:

分治,hash對映,堆排序,雙層桶劃分,Bloom Filter,bitmap,資料庫索引,mapreduce等。

具體情形都有很多不同的方案。這類題目可以到網上搜尋一下,瞭解下套路,後面就基本都會了。

平時有沒有用linux系統,怎麼檢視某個程式

ps aux|grep java 檢視java程式ps aux 檢視所有程式ps –ef|grep tomcat 檢視所有有關tomcat的程式ps -ef|grep --color java 高亮要查詢的關鍵字kill -9 19979 終止執行緒號位19979的程式複製程式碼

說一下 Innodb 和 MySIAM 的區別

MyISAM型別不支援事務處理等高階處理,而InnoDB型別支援。MyISAM型別的表強調的是效能,其執行數度比InnoDB型別更快,但是不提供事務支援,而InnoDB提供事務支援以及外部鍵等高階資料庫功能。

InnoDB不支援FULLTEXT型別的索引。

InnoDB 中不儲存表的具體行數,也就是說,執行select count(

) from table時,InnoDB要掃描一遍整個表來計算有多少行,但是MyISAM只要簡單的讀出儲存好的行數即可。注意的是,當count(
)語句包含 where條件時,兩種表的操作是一樣的。

對於AUTO_INCREMENT型別的欄位,InnoDB中必須包含只有該欄位的索引,但是在MyISAM表中,可以和其他欄位一起建立聯合索引。

DELETE FROM table時,InnoDB不會重新建立表,而是一行一行的刪除。

LOAD TABLE FROM MASTER操作對InnoDB是不起作用的,解決方法是首先把InnoDB表改成MyISAM表,匯入資料後再改成InnoDB表,但是對於使用的額外的InnoDB特性(例如外來鍵)的表不適用。

說一下jvm記憶體模型,介紹一下你瞭解的垃圾收集器

其實並沒有 jvm 記憶體模型的概念。應該是 Java 記憶體模型或者 jvm 記憶體結構,這裡面試者一定要聽清楚問的是哪個,再回答。

可以參考:

你說你是大資料方向的,瞭解哪些大資料框架

作者回答了一些zookeeper、storm、HDFS、Hbase等

其他問題

  • 100個有序的整型,如何打亂順序?

  • 如何設計一個可靠的UDP協議?

二面大概就是這些,其中storm一致性這個問題被面試官懷疑了一下,就有點緊張,其實沒答錯,所以還是要對知識掌握得更明確才行。

準備充足的三面

清明節的時候例外地沒有回家掃墓,因為知道自己的弱項是作業系統和海量資料題這塊,所以想著惡補這方面的知識,不過之後的面試意外的並沒有問到這方面的內容。

介紹專案

專案介紹完之後沒問太多

介紹一下HashMap

HashMap真的是面試高頻題,多次面試都問到了,一定要掌握。

介紹一下併發

這裡可以把整個併發的體系都說下,包括volatile、synchronized、lock、樂觀悲觀鎖、鎖膨脹、鎖降級、執行緒池等

銀行賬戶讀寫怎麼做

我說了讀寫鎖以及可能出現死鎖問題

說一下關係型資料庫和非關係型資料庫的區別

非關係型資料庫的優勢:

  1. 效能:NOSQL是基於鍵值對的,可以想象成表中的主鍵和值的對應關係,而且不需要經過SQL層的解析,所以效能非常高

  2. 可擴充套件性:同樣也是因為基於鍵值對,資料之間沒有耦合性,所以非常容易水平擴充套件。

使用場景:日誌、埋點、論壇、部落格等

關係型資料庫的優勢:

  1. 複雜查詢:可以用SQL語句方便的在一個表以及多個表之間做非常複雜的資料查詢

  2. 事務支援:使得對於安全效能很高的資料訪問要求得以實現。

使用場景:所有有邏輯關係的資料儲存

如何訪問連結串列中間節點

對於這個問題,我們首先能夠想到的就是先遍歷一遍整個的連結串列,然後計算出連結串列的長度,進而遍歷第二遍找出中間位置的資料。這種方式非常簡單。

若題目要求只能遍歷一次連結串列,那又當如何解決問題?

可以採取建立兩個指標,一個指標一次遍歷兩個節點,另一個節點一次遍歷一個節點,當快指標遍歷到空節點時,慢指標指向的位置為連結串列的中間位置,這種解決問題的方法稱為快慢指標方法。

說下程式間通訊,以及各自的區別

程式間通訊是指在不同程式之間傳播或交換資訊。方式通常有管道(包括無名管道和命名管道)、訊息佇列、訊號量、共享儲存、Socket、Streams等。

訪問淘寶網頁的一個具體流程,從獲取ip地址,到怎麼返回相關內容

先通過DNS解析到伺服器地址,然後反向代理、負載均衡伺服器等,尋找叢集中的一臺機器來真正執行你的請求。還可以介紹CDN、頁面快取、Cookie以及session等。

這個過程還包括三次握手、HTTP request中包含哪些內容,狀態碼等,還有OSI七層分層可以介紹。

伺服器接到請求後,會執行業務邏輯,執行過程中可以按照MVC來分別介紹。

服務處理過程中是否呼叫其他RPC服務或者非同步訊息,這個過程包含服務發現與註冊,訊息路由。

最後查詢資料庫,會不會經過快取?是不是關係型資料庫?是會分庫分表還是做哪些操作?

對於資料庫,分庫分表如果資料量大的話是有必要的,一般業務根據一個分表欄位進行取模進行分表,而在做資料庫操作的時候,也根據同樣的規則,決定資料的讀寫操作對應哪張表。這種也有開源的實現的,如阿里的TDDL就有這種功能。分庫分表還涉及到很多技術,比如sequence如何設定 ,如何解決熱點問題等。

最後再把處理結果封裝成response,返回給客戶端。瀏覽器再進行頁面渲染。

焦慮的hr面

之所以說hr面焦慮,是因為面試前我還在看IG的半決賽(實在複習不下),接到電話的時候分外緊張,在一些點上答得很差。

遇到什麼挫折

這種問題主要考察面試者遇見困難是否能堅持下去,並且可以看出他的解決問題的能力。

可以簡單描述挫折,並說明自己如何克服,最終有哪些收穫。

職業規劃

表明自己決心,首先自己不準備繼續求學了,必須招工作了。然後說下自己不會短期內換行業,或者換工作,自己比較喜歡,希望可以堅持幾年看自己的興趣再規劃之類的。

對阿里的認識

這個比較簡答,誇就行了。

有什麼崇拜的人嗎

我說了詹姆斯哈登,hr小姐姐居然笑了。

這個可以說一些IT大牛。

希望去哪裡就業

這個問題果斷回答該公司所在的城市啊。

其他問題

有什麼興趣愛好,能拿得上臺表演的有嗎

記憶深刻的事情

總結

提前批更多的是考察基礎知識,大公司都有自己在用的框架,你進去後基本上得重新學這些框架,所以對他們來說,基礎是否紮實才是考察的關鍵。

基礎包括: 作業系統、linxu、資料庫、資料結構、演算法、java(基礎、容器、高併發、jvm)、計算機網路等**

建議要投資知識,從寒假到現在,先後買了9個極客時間的課程、訂閱了H神的知識星球、噹噹買了四五本相關技術書籍…

雖然購買的課很多還來不及讀(慚愧)

當時我問一個Java群的師兄,學不下了怎麼辦,他說,換種姿勢繼續學,還別說,有時候失眠的時候,我都在看極客時間或知識星球催眠自己…

要對知識做好總結,雖然以前也有記錄簡書的習慣,但是大多數時候都是寫了不發表,自己做一個記憶的作用,3月份我給自己的要求就是,對每個知識點要做到能夠有自己的理解,然後寫一篇質量較好的部落格總結。

面試建議是,一定要自信,敢於表達,面試的時候我們對知識的掌握有時候很難面面俱到,把自己的思路說出來,而不是直接告訴面試官自己不懂,這也是可以加分的。


相關文章