(高頻問題)261-280 計算機 Java后端 實習 and 秋招 面試高頻問題匯總
261. 跳表相較于紅黑樹的核心優(yōu)勢分析
跳表與紅黑樹在特定場景下各有優(yōu)勢。跳表的一項顯著優(yōu)勢在于其實現的簡潔性。它主要依賴鏈表結構和隨機化技術來構建多層索引,代碼邏輯相對直接,易于理解和維護,尤其在插入和刪除操作時,不需要像紅黑樹那樣進行復雜的平衡調整,如旋轉或重新著色。
盡管紅黑樹這類平衡樹也具備 O(log N) 的時間復雜度,但其維持平衡的機制較為復雜。插入和刪除操作可能觸發(fā)多次旋轉和顏色調整,這不僅增加了代碼的實現難度,也使得調試和維護更具挑戰(zhàn)性。
在空間使用和訪問特性方面,跳表的空間復雜度可能略高于紅黑樹,但它能夠靈活調整層數以適應不同的查詢和插入模式。更重要的是,跳表的結構天然支持高效的順序訪問和范圍查詢。一旦定位到范圍的起始點,即可通過最底層的鏈表以 O(K) 的時間復雜度(K為訪問元素數量)進行快速遍歷,這在處理大規(guī)模順序數據時表現優(yōu)異。
并發(fā)控制是跳表的另一大優(yōu)勢。由于其多層鏈表的本質,跳表更容易實現高并發(fā)環(huán)境下的細粒度鎖控制,例如通過分層鎖或對鏈表節(jié)點進行精細鎖定。相比之下,紅黑樹的旋轉操作可能涉及樹的較大范圍結構調整,實現高效并發(fā)控制的難度更高,往往需要更復雜的鎖策略。
262. Lua腳本在Redis單機與集群環(huán)境下的關鍵差異
Lua腳本在Redis單機實例和集群環(huán)境下的運作機制存在顯著差異,主要體現在數據訪問范圍、鍵操作限制以及原子性保障層面。
在單機Redis中,Lua腳本的執(zhí)行是原子性的,并且可以訪問該Redis實例內的所有數據,不受任何節(jié)點或分片限制。腳本執(zhí)行期間,Redis服務器不會交錯執(zhí)行其他命令,從而確保了操作的完整性和一致性。
然而,在Redis集群模式下,Lua腳本的執(zhí)行范圍受到嚴格限制,即腳本只能在單個節(jié)點上執(zhí)行。這意味著腳本內部操作的所有鍵都必須位于同一個哈希槽(hash slot)中,也就是由同一個節(jié)點管理。如果腳本嘗試訪問分布在不同節(jié)點上的鍵,操作將會失敗。為了確保多個鍵能被同一個腳本處理,開發(fā)者需要使用哈希標簽(hash tags),例如將鍵名設計為{user:1000}:name
和{user:1000}:email
,這樣Redis集群會根據花括號內的部分(即user:1000
)計算哈希槽,保證它們落在同一節(jié)點。Redis集群共有16384個哈希槽。
關于原子性,雖然Lua腳本在集群的單個節(jié)點上的執(zhí)行依然保持原子性,但無法通過單個Lua腳本來保證跨多個節(jié)點操作的原子性。對于涉及多節(jié)點的數據一致性需求,需要在客戶端層面進行協調和管理。此外,在集群環(huán)境中,Lua腳本需要在每個節(jié)點上分別進行加載和管理,并不存在全局的腳本共享或傳播機制。
263. 兩階段提交(2PC)與三階段提交(3PC)的核心差異及3PC的優(yōu)勢
兩階段提交(2PC)和三階段提交(3PC)是分布式系統中用于保障分布式事務原子性和一致性的重要協議。它們的核心差異主要體現在協議階段的設計以及故障處理能力上。
2PC包含兩個核心階段:
- 準備階段(Prepare Phase):協調者向所有參與者發(fā)送準備請求。參與者執(zhí)行事務操作,記錄日志,但不實際提交,并向協調者返回“準備就緒”或“失敗”的響應。
- 提交階段(Commit Phase):如果所有參與者均回復“準備就緒”,協調者則發(fā)送提交請求,參與者執(zhí)行提交;若有任何參與者失敗或超時,協調者發(fā)送回滾請求,參與者執(zhí)行回滾。
3PC則引入了額外的階段,細化了提交流程:
- 可提交階段(CanCommit Phase):協調者詢問參與者是否可以執(zhí)行事務。參與者檢查自身資源和狀態(tài),回復“是”或“否”。
- 預提交階段(PreCommit Phase):若所有參與者均回復“是”,協調者發(fā)送預提交請求。參與者執(zhí)行事務操作,記錄日志,并進入“預備提交”狀態(tài),等待最終指令。
- 提交階段(DoCommit Phase):協調者根據預提交階段的反饋(或超時情況)發(fā)送提交或中止請求。參與者完成最終提交或回滾。
3PC相較于2PC的主要優(yōu)勢在于其改進的故障處理機制和減少的阻塞可能性。在2PC中,若協調者在發(fā)送提交請求后、所有參與者確認前發(fā)生故障,或者協調者與某些參與者之間網絡分區(qū),參與者會因無法確定最終狀態(tài)而陷入阻塞。3PC通過增加CanCommit和PreCommit階段,特別是PreCommit階段,引入了超時機制。如果在PreCommit階段后協調者故障或參與者未收到DoCommit指令,參與者在超時后可以自行決定提交(如果進入了PreCommit狀態(tài))或中止事務,從而降低了系統長時間阻塞的風險,提高了系統的可用性和容錯性。增加一個階段主要是為了在協調者和參與者之間建立一個更明確的“意向”狀態(tài),減少不確定性。
264. 2PC、3PC與Raft協議在分布式一致性保障中的應用場景差異
2PC、3PC和Raft協議雖然都服務于分布式系統的一致性保障,但它們解決的問題范疇和適用的具體場景有所不同。
2PC和3PC主要關注于分布式事務的原子性,確??缍鄠€獨立資源(如數據庫、消息隊列)的操作能夠作為一個整體,要么全部成功執(zhí)行,要么全部失敗回滾。這種場景通常被稱為垂直的事務一致性,強調的是一個事務在其生命周期內跨越不同階段和操作時,所有步驟的最終結果必須一致。例如,在涉及多個數據庫更新的銀行轉賬業(yè)務中,2PC被廣泛應用以保證資金從一個賬戶扣除與另一個賬戶增加這兩個操作的一致性。3PC作為2PC的改進,通過增加一個預備階段來減少協調者故障時參與者的阻塞時間,適用于對系統可用性和容錯性有更高要求的環(huán)境。
Raft協議則是一種分布式共識算法,其核心目標是讓一組服務器(節(jié)點)就某個值或一系列狀態(tài)(通常是日志條目)達成一致。它解決的是水平的節(jié)點共識問題,即在任何給定時間點,確保系統中的多個副本或節(jié)點對于某一共享狀態(tài)的認知保持一致,即使在部分節(jié)點發(fā)生故障或網絡分區(qū)的情況下,系統仍能通過多數派原則繼續(xù)安全運行。Raft的應用場景包括分布式系統的Leader選舉、日志復制、狀態(tài)機復制以及高可用的配置管理服務等。例如,在分布式數據庫或鍵值存儲中,Raft常被用來確保數據在多個副本間的一致性復制和故障切換。
265. 連接池中潛在的數據串流風險及防范措施
數據庫連接池在設計上致力于通過復用連接來提升性能,并通常能夠有效隔離不同請求對連接的使用。然而,如果連接池的管理或應用程序對連接的使用方式不當,確實存在潛在的數據串流風險,即一個線程或請求錯誤地讀取或修改了屬于另一個線程或請求的數據。
這類問題通常源于以下幾種情況:其一是連接未被正確關閉或歸還到池中,導致連接上可能殘留了前一個操作的狀態(tài)(如未提交的事務、會話變量等),當該連接被下一個請求復用時,可能發(fā)生數據混淆。其二是應用程序手動管理事務(例如,設置setAutoCommit(false)
)但未能確保每個事務都正確提交或回滾,使得一個未結束的事務狀態(tài)影響了后續(xù)使用該連接的操作。
為有效防范數據串流等連接使用問題,應采取以下關鍵措施:
務必確保每次數據庫操作完成后,連接都得到妥善關閉或釋放回池中。在Java等語言中,推薦使用try-with-resources
語句,或者在finally
塊中執(zhí)行關閉操作,以保證連接在任何情況下(包括異常發(fā)生時)都能被正確處理。對于事務管理,應確保事務的邊界清晰,并在操作完成后顯式提交(commit)或回滾(rollback)。避免將處于未決事務狀態(tài)的連接歸還給連接池。
應盡量縮短持有連接的時間,在獲取連接后迅速完成數據庫交互并立即釋放,避免在持有連接的狀態(tài)下執(zhí)行耗時的業(yè)務邏輯。選擇并使用經過廣泛驗證的、成熟的連接池實現(例如HikariCP、Druid、C3P0等),這些連接池通常內置了更完善的連接狀態(tài)清理和管理機制。
此外,合理配置連接池參數,并對連接池的運行狀態(tài)進行持續(xù)監(jiān)控,有助于及時發(fā)現和定位潛在的連接使用不當問題。
266. PCB中父子進程關系的維護機制
進程控制塊 (PCB) 是操作系統用于管理進程的核心數據結構。父子進程關系主要通過PCB中的特定字段來維護。
每個進程在其PCB中都存儲有自身的進程ID (PID)以及其父進程的ID (PPID)。通過PPID,任何進程都可以明確其父進程,這是建立父子關系的基礎。
此外,許多操作系統的PCB設計中,會包含一個指向子進程列表的指針或數據結構。該列表匯集了由當前進程創(chuàng)建的所有子進程的PCB引用,使得操作系統能夠高效地遍歷和管理特定父進程下的所有子進程。部分實現中,PCB內也可能直接包含一個**parent
指針**,直接指向其父進程的PCB,進一步鞏固這種層級關系。這些機制共同確保了操作系統能夠清晰地追蹤和管理進程間的親緣關系。
267. 多線程環(huán)境下通過鎖與條件變量實現交替打印123
題目要求啟動三個獨立的線程,分別負責循環(huán)打印數字1、2、3,并確保它們的輸出順序嚴格按照123123...的模式進行。
核心實現思路是利用一個共享變量(globalNumber
)來控制當前應由哪個線程執(zhí)行打印操作,并結合**鎖(lock
)以及條件變量機制(wait()
和 notifyAll()
)**進行線程間的同步與協作。
每個線程在進入打印邏輯前首先獲取鎖。在synchronized
代碼塊中,線程會檢查共享變量globalNumber
是否等于自身需要打印的數字。若不等于,則調用lock.wait()
方法,釋放鎖并進入等待狀態(tài)。當其他線程完成打印并修改globalNumber
后,會調用lock.notifyAll()
喚醒所有等待的線程。
被喚醒的線程會重新競爭鎖,并再次檢查條件。只有當條件滿足時(globalNumber
等于其目標數字),線程才會執(zhí)行打印操作,然后更新globalNumber
為下一個應打印的數字(例如,從1到2,從2到3,從3回到1),并再次調用lock.notifyAll()
通知其他線程。
import java.util.*; import java.lang.*; public class Main { private final Object lock = new Object(); private volatile int globalNumber = 1; // 共享變量,控制打印順序 public static void main(String[] args) { Main main = new Main(); Thread t1 = new Thread(() -> { main.printNumber(1); // 線程1打印數字1 }); Thread t2 = new Thread(() -> { main.printNumber(2); // 線程2打印數字2 }); Thread t3 = new Thread(() -> { main.printNumber(3); // 線程3打印數字3 }); t1.start(); t2.start(); t3.start(); } public void printNumber(int threadNum) { // 為了演示,這里設置為循環(huán)10次,實際題目要求無限循環(huán),可以將 for 循環(huán)改為 while(true) for (int i = 0; i < 10; i++) { synchronized (lock) { // 當全局數字不等于當前線程應打印的數字時,線程等待 while (globalNumber != threadNum) { try { lock.wait(); // 釋放鎖并進入等待狀態(tài) } catch (InterruptedException e) { Thread.currentThread().interrupt(); // 重置中斷狀態(tài) return; } } // 條件滿足,執(zhí)行打印 System.out.println(threadNum); // 更新全局數字,指向下一個應打印的數字 globalNumber = (globalNumber % 3) + 1; lock.notifyAll(); // 喚醒所有等待在該鎖上的線程 } } } }
值得注意的是,while (globalNumber != threadNum)
循環(huán)結合 lock.wait()
并非忙等待(busy-waiting)。當條件不滿足時,線程通過 wait()
釋放鎖并進入休眠狀態(tài),等待被其他線程通過 notifyAll()
喚醒。喚醒后,線程會再次檢查條件,只有滿足時才繼續(xù)執(zhí)行,否則再次等待,從而有效避免了CPU資源的空轉。
268. Kafka實現高吞吐量的關鍵技術解析
Kafka 實現卓越的吞吐量得益于多項精心設計的關鍵技術。
首先,它充分利用了順序磁盤寫入的特性。數據以追加(append-only)方式寫入磁盤上的日志文件,這種方式避免了機械硬盤磁頭的頻繁隨機尋道,其性能遠快于隨機寫入,甚至接近內存的寫入速度。
其次,Kafka 是一個分布式架構。消息數據可以分散存儲在多個 Broker 節(jié)點上,并且每個主題(Topic)可以劃分為多個分區(qū)(Partition)。這種設計使得讀寫負載能夠均衡地分布到集群中的各個節(jié)點和分區(qū),從而實現水平擴展和并行處理,大幅提升整體處理能力。
數據傳輸效率方面,Kafka 采用了零拷貝(Zero-copy)技術。在數據從磁盤發(fā)送到網絡接口(NIC)的過程中,通過操作系統的支持,數據可以直接在內核空間進行傳輸,避免了在用戶空間和內核空間之間的多次數據拷貝,顯著降低了CPU開銷和上下文切換的延遲。
此外,Kafka 支持消息的批量處理。生產者可以將多條消息累積成一個批次(batch)后一次性發(fā)送給Broker,消費者也可以批量從Broker拉取消息。這種機制減少了網絡請求次數和I/O操作的頻率,分攤了單條消息的處理開銷。結合高效的自定義二進制網絡通信協議和可選的消息壓縮功能(如Snappy, Gzip, LZ4),進一步減少了網絡傳輸的數據量和序列化/反序列化開銷,共同構成了Kafka高吞吐量的堅實基礎。
269. Web瀏覽器如何確定未指定端口號的服務器目標端口
當用戶在瀏覽器中輸入網址(例如 www.taobao.com
)且未顯式指定端口號時,瀏覽器會依據URL中的協議類型自動使用該協議的默認端口號。
對于 HTTP 協議,其默認端口是 80。因此,訪問 http://www.taobao.com
時,瀏覽器會嘗試連接淘寶服務器的80端口。對于 HTTPS 協議(安全的HTTP),其默認端口是 443。因此,訪問 https://www.taobao.com
時,瀏覽器會嘗試連接淘寶服務器的443端口。
域名系統 (DNS) 的核心職責是將用戶易于記憶的域名解析為服務器的IP地址。它通常不直接參與端口號的解析(SRV記錄是一種例外情況,允許服務發(fā)現包含端口信息,但瀏覽器在常規(guī)網頁訪問中較少依賴此機制)。瀏覽器在通過DNS獲取到IP地址后,便會結合協議類型使用上述默認端口與服務器建立連接。
如果服務器端的應用程序實際監(jiān)聽的是一個非標準端口(例如,一個Java Web應用可能部署在8080端口),為了讓用戶能夠通過標準的80或443端口(即不帶端口號的URL)訪問,通常會配置一個反向代理服務器(如 Nginx 或 Apache HTTP Server)。這個反向代理服務器會監(jiān)聽公共的80或443端口,接收到用戶的請求后,再將這些請求透明地轉發(fā)到內部應用實際監(jiān)聽的非標準端口上。
270. HTTPS客戶端驗證與信任服務器證書的機制
HTTPS客戶端(通常是瀏覽器或操作系統級別的網絡庫)信任服務器SSL/TLS證書的機制是建立在一個預先建立的信任體系之上的。
其核心在于客戶端內置了一組受信任的根證書頒發(fā)機構(Root Certificate Authorities, Root CAs)的公共證書。這些根CA是全球公認的、經過嚴格審核的可信實體,其根證書在操作系統或瀏覽器安裝時就已經預置。
當客戶端嘗試與一個
剩余60%內容,訂閱專欄后可繼續(xù)查看/也可單篇購買
曾獲多國內大廠的 ssp 秋招 offer,且是Java5年的沉淀老兵(不是)。專注后端高頻面試與八股知識點,內容系統詳實,覆蓋約 30 萬字面試真題解析、近 400 個熱點問題(包含大量場景題),60 萬字后端核心知識(含計網、操作系統、數據庫、性能調優(yōu)等)。同時提供簡歷優(yōu)化、HR 問題應對、自我介紹等通用能力??紤]到歷史格式混亂、質量較低、也在本地積累了大量資料,故準備從頭重構專欄全部內容