大家好,我是小林。
今天分享一篇一位同學暑期實習面試阿里Java后端崗位的一面的面經。
主要拷打了項目+Java 集合+Java并發+網絡+mysql,一場面試大概問了 20 個題目,問的還是比較基礎,不算太難。
問題記錄
自我介紹
balabala(略)
簡歷上有兩個項目,選一個你比較熟悉的介紹
balabala(略)
項目用到了哪個線程實現類?
為什么要使用這個線程類?
這個實現類可以設置定期的執行任務,它支持定時或周期性執行任務,比如每隔 10 秒鐘執行一次任務,我通過這個實現類設置定期執行任務的策略。
你還了解別的線程實現類嗎?
除了這個之外,還有就是newSingleThreadExecuter,別的就不太熟悉了。
小林補充:
除了 ScheduledThreadPool 線程池之外,還有 4 種常見的線程池如下:
FixedThreadPool:它的核心線程數和最大線程數是一樣的,所以可以把它看作是固定線程數的線程池,它的特點是線程池中的線程數除了初始階段需要從 0 開始增加外,之后的線程數量就是固定的,就算任務數超過線程數,線程池也不會再創建更多的線程來處理任務,而是會把超出線程處理能力的任務放到任務隊列中進行等待。而且就算任務隊列滿了,到了本該繼續增加線程數的時候,由于它的最大線程數和核心線程數是一樣的,所以也無法再增加新的線程了。
CachedThreadPool:可以稱作可緩存線程池,它的特點在于線程數是幾乎可以無限增加的(實際最大可以達到 Integer.MAX_VALUE,為 2^31-1,這個數非常大,所以基本不可能達到),而當線程閑置時還可以對線程進行回收。也就是說該線程池的線程數量不是固定不變的,當然它也有一個用于存儲提交任務的隊列,但這個隊列是 SynchronousQueue,隊列的容量為0,實際不存儲任何任務,它只負責對任務進行中轉和傳遞,所以效率比較高。
SingleThreadExecutor:它會使用唯一的線程去執行任務,原理和 FixedThreadPool 是一樣的,只不過這里線程只有一個,如果線程在執行任務的過程中發生異常,線程池也會重新創建一個線程來執行后續的任務。這種線程池由于只有一個線程,所以非常適合用于所有任務都需要按被提交的順序依次執行的場景,而前幾種線程池不一定能夠保障任務的執行順序等于被提交的順序,因為它們是多線程并行執行的。
SingleThreadScheduledExecutor:它實際和 ScheduledThreadPool 線程池非常相似,它只是 ScheduledThreadPool 的一個特例,內部只有一個線程。
看你項目上有用到雪花算法,你為什么要使用雪花算法?
除了考慮使用主鍵自增保持主鍵的唯一性外,我就使用到了雪花算法,算出一個不會重復的數做為id,保證主鍵唯一。
那你還了解別的生成主鍵的策略嗎,你覺得他們能代替雪花算法嗎
除了主鍵自增和雪花算法,別的我暫時沒了解的,但是只要是能保證主鍵唯一的主鍵生成策略都可以使用
小林補充:
除了雪花算法之外,還有很多優秀的互聯網公司也提供了唯一 ID 生成的方案或框架,比如美團開源的 Leaf ,百度開源的 UidGenerator 等等。
UUID 雖然也可以保證唯一性,但是 UUID 的值是隨機的,無序的,不太適合作為主鍵,因為隨機插入,可能會引起頁分裂的問題,從而影響查詢性能。
List的實現類
ArrayList、Vector、LinkedList
小林補充:
Java中的List接口有多個實現類,常用的包括:
ArrayList:基于動態數組實現,優勢在于支持隨機訪問和快速插入/刪除元素,適用于頻繁讀取和遍歷的場景。
LinkedList:基于雙向鏈表實現,優勢在于支持快速插入/刪除元素,適用于頻繁插入/刪除元素的場景。
Vector:和ArrayList類似,但由于其線程安全性,適用于多線程環境。
Stack:基于Vector實現,是一個后進先出(LIFO)的數據結構,適用于需要按照后進先出順序處理元素的場景。
List和Set的區別
List是有序的,Set是無序的
List可以存放相同的元素,Set不可以存放重復的元素
小林補充:
順序:List是有序的集合,它可以按照元素插入的順序進行存儲和訪問。而Set是無序的集合,元素在集合中的位置是不固定的。
重復元素:List允許存儲重復的元素,即可以有多個相同的對象。Set不允許存儲重復的元素,即每個對象在集合中只能出現一次。
實現類:List的常用實現類有ArrayList和LinkedList,分別使用數組和鏈表作為底層數據結構。Set的常用實現類有HashSet、LinkedHashSet和TreeSet,分別基于哈希表、鏈表+哈希表和紅黑樹實現。
性能:由于底層數據結構的差異,List和Set在增加、刪除、查找等操作上的性能表現有所不同。例如,ArrayList在隨機訪問元素時性能較好,而LinkedList在插入和刪除元素時性能較好。HashSet在查找、添加和刪除元素時性能較好,但不保證元素順序。TreeSet在保持元素排序的同時,也能提供較好的查找性能。
針對你說的List和Set的性質,那你會用這兩種結構解決哪些問題
對于取消重復數據的場景,選擇set,對于只是保存數據、或者需要按存儲順序進行訪問的場景使用List。
小林補充:
List(列表)適用于以下場景:
有序數據:列表中的元素按照插入順序存儲,因此適用于需要保持元素順序的場景。
允許重復元素:列表允許存儲重復的元素,因此適用于需要統計元素出現次數的場景。
需要根據索引進行查找、插入和刪除操作:列表允許通過索引值直接訪問、插入或刪除元素,適用于需要頻繁進行這些操作的場景。
Set(集合)適用于以下場景:
去重:集合中的元素不能重復,因此適用于去除數據中重復元素的場景。
無需關心元素順序:集合中的元素沒有固定順序,適用于元素順序無關緊要的場景。
快速判斷元素是否存在:集合提供了高效率的查找算法,適用于需要快速判斷某個元素是否存在于數據集中的場景。
集合運算:集合支持交集、并集、差集等運算,適用于需要進行這些運算的場景。
常用的網絡狀態碼有哪些
100開頭是表示協議執行的中間狀態,一般不常用,400開頭的表示協議執行失敗,例如404是指服務端找不到頁面的請求地址,500是協議的完成(這個沒記住答錯了,面試官提示了,說那200表示什么)。
小林補充:
常用的網絡狀態碼分為五類,分別是:
1xx(信息):表示接收到請求,需要繼續處理。
100 Continue:繼續,客戶端應繼續其請求。
2xx(成功):表示請求已成功被服務器接收、理解和接受。
200 OK:請求成功,請求所希望的響應頭或數據體將隨此響應返回。
201 Created:請求已成功,并因此創建了一個新的資源。
204 No Content:無內容,服務器成功處理,但未返回內容。
3xx(重定向):需要后續操作才能完成這一請求。
301 Moved Permanently:永久重定向,請求的資源已被永久移動到新位置。
302 Found:臨時重定向,請求的資源臨時從不同位置響應。
304 Not Modified:資源未修改,使用緩存的資源。
4xx(客戶端錯誤):請求包含錯誤語法或無法完成。
400 Bad Request:客戶端請求的語法錯誤,服務器無法理解。
401 Unauthorized:請求需要用戶驗證。
403 Forbidden:服務器理解請求客戶端的請求,但是拒絕執行它。
404 Not Found:請求的資源無法在服務器上找到。
5xx(服務器錯誤):服務器未能實現合法的請求。
500 Internal Server Error:服務器內部錯誤,無法完成請求。
501 Not Implemented:服務器不支持請求的功能。
503 Service Unavailable:服務器暫不可用,可能是服務器過載或停機維護。
ps:《HTTP 常見面試題》完整詳細講解:https://xiaolincoding.com/network/2_http/http_interview.html
流量控制和擁塞控制的原理
流量控制是服務端和客戶端協議的窗口實現,在客戶端發送數據的時候,服務端返回窗口的容量,客戶端通過容量來調整發送信息的大小
擁塞控制是通過滑動窗口實現,服務端只接收擁塞窗口大小內的數據,客服端發送的報文丟失后,沒有收到服務端的確認信息,就將沒有收到確認信息的保溫再發送。
小林補充:
TCP一種面向連接的、可靠的傳輸層協議。為了確保數據的有效傳輸,TCP 提供了兩種重要的控制機制:流量控制和擁塞控制。
流量控制(Flow Control)
流量控制的主要目的是防止發送方向接收方發送過多數據,導致接收方處理不過來。TCP 使用滑動窗口機制來實現流量控制。在 TCP 連接中,接收方為每個連接分配一個接收緩沖區。接收方通過通知發送方自己的窗口大小,告知發送方可以發送的數據量。窗口大小表示接收方當前能接受的數據字節數。
滑動窗口的工作原理如下:
發送方根據接收方的窗口大小來確定發送的數據量。
當接收方收到數據后,發送確認報文,同時更新窗口大小。
發送方收到確認報文后,更新已發送但未確認的數據量,并根據新的窗口大小調整發送速率。
擁塞控制(Congestion Control)
擁塞控制的目的是防止過多的數據進入網絡,導致網絡擁塞。TCP 使用四種算法來實現擁塞控制:慢開始、擁塞避免、快速重傳和快速恢復。
慢開始:發送方初始擁塞窗口設置為一個較小的值。隨后,每收到一個確認報文,擁塞窗口大小加倍。這樣,發送速率會以指數形式增長,直到達到一個閾值(ssthresh)。
擁塞避免:當擁塞窗口到達閾值后,發送方轉入擁塞避免階段,窗口大小每經過一個往返時間(RTT)就增加1。這樣,擁塞窗口的大小呈線性增長,避免了網絡擁塞。
快速重傳:當發送方連續收到三個重復的確認報文,表示可能有一個數據包丟失。此時,發送方立即重傳丟失的數據包,而不是等待超時重傳。
快速恢復:快速重傳后,發送方降低擁塞窗口閾值,然后進入擁塞避免階段。這樣可以在丟包后盡快恢復傳輸速率。
通過這兩種控制機制,TCP 能確保在各種網絡條件下有效、可靠地傳輸數據。
ps:《TCP 重傳、滑動窗口、流量控制、擁塞控制》完整詳細講解:https://xiaolincoding.com/network/3_tcp/tcp_feature.html
一條url請求頁面的執行過程
(這塊我答的不太好,忘了dns服務器的名字)瀏覽器先解析url地址,然后生成http消息,生成的消息需要知道ip地址才能發送,就先去瀏覽器的緩存中查詢,沒有的話查看操作系統的緩存,如果還是沒有就在本地dns中查詢,本地dns查詢不到后會先訪問根dns,根dns查詢的是存放這個ip的二級dns服務器(忘了名字),二級dns服務器會向對應的權威dns服務器查詢,權威dns服務器會向本地返回ip地址,然后本地通過這個ip地址和請求的服務端建立起tcp連接,服務端向本地發送請求的資源。
小林補充:
ps:《鍵入網址到網頁顯示,期間發生了什么?》完整詳細講解:https://xiaolincoding.com/network/1_base/what_happen_url.html
TCP是如何建立連接的
三次握手
1、客戶端發送請求建立連接報文,報文的SYN=1
2、服務端收到后,發送連接報文,報文的SYN=1,并且發送一個序號字段
3、客戶端收到后報文后,客戶端到服務端的連接已經建立,客戶端發送報文對上一個報文的序號端進行確認
小林補充:
TCP 是面向連接的協議,所以使用 TCP 前必須先建立連接,而建立連接是通過三次握手來進行的。三次握手的過程如下圖:
TCP 三次握手
一開始,客戶端和服務端都處于 CLOSE 狀態。先是服務端主動監聽某個端口,處于 LISTEN 狀態
客戶端會隨機初始化序號(client_isn),將此序號置于 TCP 首部的「序號」字段中,同時把 SYN 標志位置為 1,表示 SYN 報文。接著把第一個 SYN 報文發送給服務端,表示向服務端發起連接,該報文不包含應用層數據,之后客戶端處于 SYN-SENT 狀態。
服務端收到客戶端的 SYN 報文后,首先服務端也隨機初始化自己的序號(server_isn),將此序號填入 TCP 首部的「序號」字段中,其次把 TCP 首部的「確認應答號」字段填入 client_isn + 1, 接著把 SYN 和 ACK 標志位置為 1。最后把該報文發給客戶端,該報文也不包含應用層數據,之后服務端處于 SYN-RCVD 狀態。
客戶端收到服務端報文后,還要向服務端回應最后一個應答報文,首先該應答報文 TCP 首部 ACK 標志位置為 1 ,其次「確認應答號」字段填入 server_isn + 1 ,最后把報文發送給服務端,這次報文可以攜帶客戶到服務端的數據,之后客戶端處于 ESTABLISHED 狀態。
服務端收到客戶端的應答報文后,也進入 ESTABLISHED 狀態。
ps:《TCP 三次握手與四次揮手面試題》完整詳細講解:https://xiaolincoding.com/network/3_tcp/tcp_interview.html
http和https的區別
1、https是需要通過CA申請才能獲得,所以數量是比較少的
2、http發送的報文是明文,所以不安全,https在傳輸層之上加了ssl協議
小林補充:
HTTP是一種用于傳輸超文本的協議,數據傳輸是明文的,不具備加密和安全性。HTTP使用的端口號是 80
HTTPS是在HTTP的基礎上加入了SSL/TLS協議進行加密和身份驗證的安全版本。它使用加密的SSL/TLS協議進行數據傳輸,保證了數據的機密性和完整性。HTTPS使用的端口號是443。
ps:《 HTTP 常見面試題》完整詳細講解:https://xiaolincoding.com/network/2_http/http_interview.html
數據庫的索引
B+樹索引,hash索引、全文索引
B+樹索引的話是innodb采用的索引,索引的葉子結點上是數據,非葉子結點是索引信息
hash索引單個的查找效率很高
為什么采用B+樹索引,它有什么優點
這里我將B+樹和B樹、紅黑樹做了比較。
B+樹相對于B樹,只有葉子結點存儲的是數據信息,非葉子結點都是索引信息,所以在查找時加載到內存中的數據少,B+樹的增刪相對于B樹來說比較穩定,不會發生頻繁的父子結點替換,B+樹的葉子結點是連接的,所以很容易實現范圍查詢
B+樹相對于紅黑樹,首先B+樹的層高比較小,意味著讀取數據時IO磁盤的次數比較少,紅黑樹增刪結點時需要保持子樹的穩定性,增刪的效率很低,B+樹更容易實現范圍查詢。
小林補充:
樹的高度決定于磁盤 I/O 操作的次數,因為樹是存儲在磁盤中的,訪問每個節點,都對應一次磁盤 I/O 操作,也就是說樹的高度就等于每次查詢數據時磁盤 IO 操作的次數,所以樹的高度越高,就會影響查詢性能。
B 樹和 B+ 都是通過多叉樹的方式,會將樹的高度變矮,所以這兩個數據結構非常適合檢索存于磁盤中的數據。
但是 MySQL 默認的存儲引擎 InnoDB 采用的是 B+ 作為索引的數據結構,原因有:
B+ 樹的非葉子節點不存放實際的記錄數據,僅存放索引,因此數據量相同的情況下,相比存儲即存索引又存記錄的 B 樹,B+樹的非葉子節點可以存放更多的索引,因此 B+ 樹可以比 B 樹更「矮胖」,查詢底層節點的磁盤 I/O次數會更少。
B+ 樹有大量的冗余節點(所有非葉子節點都是冗余索引),這些冗余索引讓 B+ 樹在插入、刪除的效率都更高,比如刪除根節點的時候,不會像 B 樹那樣會發生復雜的樹的變化;
B+ 樹葉子節點之間用鏈表連接了起來,有利于范圍查詢,而 B 樹要實現范圍查詢,因此只能通過樹的遍歷來完成范圍查詢,這會涉及多個節點的磁盤 I/O 操作,范圍查詢效率不如 B+ 樹。
ps:《為什么 MySQL 采用 B+ 樹作為索引?》完整詳細講解:https://xiaolincoding.com/mysql/index/why_index_chose_bpuls_tree.html
數據庫中事務可能帶來的問題
有臟讀、不可重復讀、幻讀三個問題:
臟讀:一個事務讀取另一個事務沒有提交的數據
不可重復讀:一個事務重復讀取一條數據時發現讀取到的數據不相同
幻讀:一個事務后讀取的數據相比之前讀取的數據中多了一些數據
小林補充:
MySQL 服務端是允許多個客戶端連接的,這意味著 MySQL 會出現同時處理多個事務的情況。
那么在同時處理多個事務的時候,就可能出現臟讀(dirty read)、不可重復讀(non-repeatable read)、幻讀(phantom read)的問題。
1、臟讀:如果一個事務「讀到」了另一個「未提交事務修改過的數據」,就意味著發生了「臟讀」現象。
舉個栗子,假設有 A 和 B 這兩個事務同時在處理,事務 A 先開始從數據庫中讀取小林的余額數據,然后再執行更新操作,如果此時事務 A 還沒有提交事務,而此時正好事務 B 也從數據庫中讀取小林的余額數據,那么事務 B 讀取到的余額數據是剛才事務 A 更新后的數據,即使沒有提交事務。
圖片
因為事務 A 是還沒提交事務的,也就是它隨時可能發生回滾操作,如果在上面這種情況事務 A 發生了回滾,那么事務 B 剛才得到的數據就是過期的數據,這種現象就被稱為臟讀。
2、不可重復讀:在一個事務內多次讀取同一個數據,如果出現前后兩次讀到的數據不一樣的情況,就意味著發生了「不可重復讀」現象。
舉個栗子,假設有 A 和 B 這兩個事務同時在處理,事務 A 先開始從數據庫中讀取小林的余額數據,然后繼續執行代碼邏輯處理,在這過程中如果事務 B 更新了這條數據,并提交了事務,那么當事務 A 再次讀取該數據時,就會發現前后兩次讀到的數據是不一致的,這種現象就被稱為不可重復讀。
圖片
3、幻讀:在一個事務內多次查詢某個符合查詢條件的「記錄數量」,如果出現前后兩次查詢到的記錄數量不一樣的情況,就意味著發生了「幻讀」現象。
舉個栗子,假設有 A 和 B 這兩個事務同時在處理,事務 A 先開始從數據庫查詢賬戶余額大于 100 萬的記錄,發現共有 5 條,然后事務 B 也按相同的搜索條件也是查詢出了 5 條記錄。
圖片
接下來,事務 A 插入了一條余額超過 100 萬的賬號,并提交了事務,此時數據庫超過 100 萬余額的賬號個數就變為 6。然后事務 B 再次查詢賬戶余額大于 100 萬的記錄,此時查詢到的記錄數量有 6 條,發現和前一次讀到的記錄數量不一樣了,就感覺發生了幻覺一樣,這種現象就被稱為幻讀。
ps:《事務隔離級別是怎么實現的?》完整詳細講解:https://xiaolincoding.com/mysql/transaction/mvcc.html
通過什么隔離級別解決這些問題
臟讀:讀寫已提交
不可重復讀:可重復讀
幻讀:串行化
小林補充:
針對不同的隔離級別,并發事務時可能發生的現象也會不同。
圖片
也就是說:
在「讀未提交」隔離級別下,可能發生臟讀、不可重復讀和幻讀現象;
在「讀提交」隔離級別下,可能發生不可重復讀和幻讀現象,但是不可能發生臟讀現象;
在「可重復讀」隔離級別下,可能發生幻讀現象,但是不可能臟讀和不可重復讀現象;
在「串行化」隔離級別下,臟讀、不可重復讀和幻讀現象都不可能會發生。
所以,要解決臟讀現象,就要升級到「讀提交」以上的隔離級別;要解決不可重復讀現象,就要升級到「可重復讀」的隔離級別,要解決幻讀現象不建議將隔離級別升級到「串行化」。
不同的數據庫廠商對 SQL 標準中規定的 4 種隔離級別的支持不一樣,有的數據庫只實現了其中幾種隔離級別,我們討論的 MySQL 雖然支持 4 種隔離級別,但是與SQL 標準中規定的各級隔離級別允許發生的現象卻有些出入。
MySQL 在「可重復讀」隔離級別下,可以很大程度上避免幻讀現象的發生(注意是很大程度避免,并不是徹底避免),所以 MySQL 并不會使用「串行化」隔離級別來避免幻讀現象的發生,因為使用「串行化」隔離級別會影響性能。
ps:《事務隔離級別是怎么實現的?》完整詳細講解:https://xiaolincoding.com/mysql/transaction/mvcc.html
mysql的隔離級別是什么?mysql是如何實現的?
不可重復讀,但是很大程度上避免幻讀
快照讀(只讀):MVCC
當前讀(更新操作):記錄鎖+間隙鎖
小林補充:
MySQL InnoDB 引擎的默認隔離級別雖然是「可重復讀」,但是它很大程度上避免幻讀現象(并不是完全解決了),解決的方案有兩種:
針對快照讀(普通 select 語句),是通過 MVCC 方式解決了幻讀,因為可重復讀隔離級別下,事務執行過程中看到的數據,一直跟這個事務啟動時看到的數據是一致的,即使中途有其他事務插入了一條數據,是查詢不出來這條數據的,所以就很好了避免幻讀問題。
針對當前讀(select ... for update 等語句),是通過 next-key lock(記錄鎖+間隙鎖)方式解決了幻讀,因為當執行 select ... for update 語句的時候,會加上 next-key lock,如果有其他事務在 next-key lock 鎖范圍內插入了一條記錄,那么這個插入語句就會被阻塞,無法成功插入,所以就很好了避免幻讀問題。
ps:《事務隔離級別是怎么實現的?》完整詳細講解:https://xiaolincoding.com/mysql/transaction/mvcc.html
算法
寫一個數據庫的多表聯查問題
沒寫出來(平時寫的少,只知道命令,不熟練),講解下思路
責任編輯:彭菁
-
數據
+關注
關注
8文章
7139瀏覽量
89569 -
存儲
+關注
關注
13文章
4353瀏覽量
86165 -
線程池
+關注
關注
0文章
57瀏覽量
6893
原文標題:阿里問的相當基礎!
文章出處:【微信號:小林coding,微信公眾號:小林coding】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論