歷經三個月的緊張複習,秋招終於進了百度,附上面經分享但願能夠幫助你們

當你開始開始去跳槽面試的時候,明明只是一份15K的工做,卻問你會不會多線程,懂不懂高併發,火箭造得讓你猝及不防,結果就是涼涼;現現在市場,多線程、高併發編程、分佈式、負載均衡、集羣等能夠說是如今高級後端開發求職的必備技能。
在這裏插入圖片描述
關於Java基礎,Spring,多線程、高併發編程、分佈式、負載均衡、集羣等這些資料我都整理好了,須要的自行領取點擊這裏~暗號博客園~。
在這裏插入圖片描述java

                                     百度一面1

String/StringBuffer和StringBuilder的區別

  1. String是由final修飾的類,同時它是由byte(9+)或者char(8-)數組組成的,這些數組也是final的linux

  2. StringBuffer是線程安全的,StringBuilder是線程不安全的。StringBuffer是經過synchronized的方法級別來實現的面試

  3. 對於StringBuffer和StringBuilder來講,他們有一個共同的父類,即AbstractStringBuilder,他們的屬性和類都沒有final修飾,因此致使了他們是可變的。相對來講,StringBuffer有本身的cache,保證了查詢的性能,這個cache在builder中是沒有的算法

接口和抽象類

  1. 在JDK5以前,接口和抽象類在語法層面上有着顯著的區別:接口不能有本身的方法體,同時接口只能是public的;抽象類能夠有本身實現的方法,同時抽象類也能夠有空方法數據庫

  2. 隨着JDK的升級,在Java8時,接口能夠有default方法,到了Java9以後,接口也能夠有本身的私有方法。接口除了屬性默認是public final的以外,幾乎和抽象類在語法層面,沒有任何區別編程

  3. 因此對於這二者的區別,咱們要站在更高的的角度,從設計的層面去看它們之間的區別。在我看來接口的設計是自上而下的,而抽象類的設計是自下而上的。設計模式中的模板方法模式,就是用抽象類的一個較好的體現。後端

  1. Java中的鎖其實分爲兩大類,一個是悲觀鎖,一個是樂觀鎖。悲觀鎖指的是synchronized家族的,使用的時候包括Object#notify()、Object#notifyAll()和Object#wait()。樂觀鎖指的是由CAS包延伸出來的一系列鎖,包括J.U.C中的ReentrantLock、ReentrantReadWriteLock,與之配合使用的是Condition設計模式

  2. 對於Synchronized來講,它的鎖粒度是對象級別的,默認是Class對象,也能夠是咱們指定的實例對象。當修飾方法的時候會在字節碼的flags中代表爲ACC_SYNCHRONIZED標識,該標識指明瞭該方法是一個同步方法,JVM 經過該 ACC_SYNCHRONIZED 訪問標誌來辨別一個方法是否聲明爲同步方法,若是有設置,則須要先得到監視器鎖。當修飾代碼塊時,會在字節碼中經過 monitorenter 和 monitorexit 執行來進行加鎖。當線程執行到 monitorenter 的時候要先得到所鎖,才能執行後面的方法。Synchronized隨着JDK的演變作出了一系列優化,如輕量級鎖,鎖粗化,鎖消除,自旋鎖等等。數組

  3. 對於ReentrantLock來講,是一種可重入鎖。它對應兩個內部類分別表示公平鎖和非公平鎖,都繼承自Sync,而Sync繼承自AQS。於公平鎖的tryAcquire()來講,它比非公平鎖多了一個!hasQueuedPredecessors()的判斷,即檢查若是當前線程位於隊列的最前面或隊列爲空,纔會讓它得到鎖緩存

  4. 相對於Synchronized,ReentrantLock須要手動獲取釋放,支持公平鎖,選擇性通知等等功能。

大概說下集合,hashMap的併發問題,HashMap中key爲NULL時存的位置

  1. Java中的集合分爲兩部分,一個是java.util包下的集合,包括list,vector,map,set等,還有一個就是J.U.C包下的併發集合

  2. 對於list來講,分爲數組存儲和鏈表存儲,分別是ArrayList和LinkedList(數組存儲中還有vector,不過其效率較低通常不用)。對於Map來講,分爲HashMap,WeakHashMap、TreeMap和LinkedHashMap。對於HashMap來講,它經過拉鍊法來解決hash衝突的問題;對於TreeMap來講,他經過紅黑樹來對key進行排序;對於LinkedHashMap來講,它經過鏈表記錄了每一個key插入的順序,能夠經過它實現LRU算法;對於WeakHashMap來講,它相似於ThreadLocal中的Entry,都繼承了WeakReference,當key不被引用的時候,能夠下次GC的時候刪除;對於Set來講,它的幾個派生類都是由對應Map實現的

  3. HashMap在1.7版本時,當Entry數組多線程擴容時,由於使用的是頭插法,會致使出現一個循環的單鏈表,致使get死循環的問題(1.8時已經修復)。除此以外,併發put元素時有可能致使覆蓋問題。

  4. HashMap中key爲null,hash的結果是0,它會被保存在entry[0]上

NIO和BIO

  1. IO複用模型 NIO(select,poll,epoll):Java NIO以前用的select,如今用的epoll。用戶發出IO請求以後,有一個select線程去管理這些請求(socket),它會不斷阻塞輪詢內核關於某事件的數據是否能準備好,沒有事件準備好則會一直阻塞。適合鏈接數較多的狀況;select基於long數組,將內核和用戶態拷貝消耗大,有1024的限制;poll基於鏈表,沒有大小限制;epoll基於map,經過事件通知方式,每當fd就緒,系統註冊的回調函數就會被調用,將就緒fd放到ready隊列裏面,時間複雜度O(1)須要注意的是:多路複用IO模型是經過輪詢的方式來檢測是否有事件到達,而且對到達的事件逐一進行響應。所以對於多路複用IO模型來講,一旦事件響應體很大,那麼就會致使後續的事件遲遲得不處處理,而且會影響新的事件輪詢

  2. Java目前爲止支持3種IO,分別是BIO,NIO和AIO。BIO對應OS的阻塞式IO,NIO對應着OS中的多路複用模型,AIO則是異步IO模型。

  3. 阻塞式IO模型 BIO:當用戶線程發出IO請求以後,內核會去查看數據是否就緒,若是沒有就緒就會等待數據就緒,而用戶線程就會處於阻塞狀態,用戶線程交出CPU。當數據就緒以後,內核會將數據拷貝到用戶線程,並返回結果給用戶線程,用戶線程才解除block狀態,如socket.accept()

線程狀態及轉換

  1. 線程共有六種基本狀態,分別是NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED

  2. RUNNABLE:分爲運行態和就緒態。就緒態即在等待一些資源如CPU

  3. BLOCKED:等待monitor的鎖,通常是要進入synchronized塊或者方法中的線程。這對應着synchronized中對象的MonitorObject的_EntryList隊列

  4. WAITING:當線程調用了Object.wait(),Thread.join()或者LockSupport.park()方法時,進入等待狀態,直到使用notify()/notifyAll(),LockSupport.unpark()或者調用方法的線程結束

  5. TIMED_WAITING:是一種特殊的等待狀態,當線程調用了Thread.sleep(long),Object.wait(long), Thread.join(long), LockSupport.parkNanos(long)或者LockSupport.parkUntil(long)方法時,進入超時等待狀態

  6. TERMINATED:線程已經完成執行,進入結束狀態

  7. NEW:線程被new出來,可是沒有調用start方法

線程池

  1. 線程池是一種池化技術,下降資源消耗,提升響應速度,提升現成的可管理性

  2. 線程池有七個參數,分別是corePoolSize,maxmumPoolSize,keepAliveTime,unit,workQueue,threadFactory,handle,當任務來臨時,線程池的線程數會逐漸增大到core,而後把多餘的放到隊列中,若是超過隊列長度,則增長線程池數目至max,若是還繼續增長,則經過handle進行拒絕。當線程空閒time時間後,會銷燬到core的數目。

  3. J.U.C提供四種常見的線程池,分別是fixed(執行固定線程數目),single(執行單個任務),cache(執行多個短時間任務)和scheduled(執行週期性任務)。前兩個等待隊列無限長,後兩個最大線程數無限大

緩存一致性

  1. 只要有緩存,就存在緩存一致性問題

  2. 先更新數據庫,再更新緩存的話:若是A更新數據庫,接着B更新數據庫,接着B更新緩存,接着A更新緩存,形成了數據庫和緩存不一致的狀況;一樣,先更新緩存,後更新數據庫也有同樣的問題

  3. 先刪除緩存,再更新數據庫:若是A刪除了緩存,接着B去查數據庫,B把髒值填入緩存中,A再更新數據庫,此時緩存已經有值了,致使緩存和數據庫不一致

  4. 採用延時雙刪的方案,即先刪除緩存,再更新數據庫,而後延時再刪除緩存。當緩存刪除失敗的時候,能夠將失敗的key發送至消息隊列,從新消費

有序數組的合併

                                          百度二面1

數據庫索引

  1. 對於MySQL來講,其索引是B+樹的結構,二叉搜索樹保證了索引是有序的,平衡二叉樹保證了索引不會退化到鏈表的極端狀況,B樹下降了平衡二叉樹的嚴格性,提升了構建效率,B+樹將值放到葉子節點中,是的樹變矮,提升IO效率

  2. 除此B+樹以外,還有Hash索引,全文索引,R-Tree索引。能夠經過B+Tree索引建立業務性質的自適應Hash索引(這個Innodb也有必定的優化,其中騰訊在11月份還貢獻了點優化的代碼,詳情能夠查看騰訊技術工程公衆號#一個即將寫入MySQL源碼的官方bug解決之路)

  3. 索引還有一些概念如:聚簇索引,組合索引,主鍵索引,索引下推,惟一索引,最左原則,覆蓋索引 ,前綴索引

  4. 一些索引失效的場景包括:like違背最左原則,類型不匹配,使用!等

AQS

  1. AQS經過內置的FIFO雙端雙向鏈表來完成獲取資源線程的排隊工做,雙端雙向鏈表。該隊列由一個一個的Node結點組成,每一個Node結點維護一個prev引用和next引用,分別指向本身的前驅和後繼結點。AQS維護兩個指針,分別指向隊列頭部head和尾部tail。該隊列中的Node有五種狀態,分別是CANCELLED,SIGNAL, CONDITION,PROPAGATE和初始狀態

  2. 從使用上來講,AQS的功能能夠分爲兩種:獨佔(如ReentrantLock)和共享(如Semaphore/CountDownLatch,CyclicBarrier/ReadWriteLock)。ReentrantReadWriteLock能夠當作是組合式,它對讀共享,寫獨佔

  3. 除了ReentrantLock外,還有三種經常使用的AQS組件,分別是Semaphore,CountDownLatch和CyclicBarrier

字段太長爲何不去創建索引

  1. 字段過長的話會使得索引的存儲變得很大,同時查找起來也會下降效率

  2. 因此咱們不如使用前綴索引來儘量下降索引的長度

  3. 寫一個LRU,併發的LRU該怎麼寫

                                       百度三面
    • 1

  4. 分佈式鎖

  5. 對於單機的鎖來講,它只是一個全部線程/進程都能看到的一個標記而已。對於Java來講,synchronized是在對象頭設置標記,Lock 接口的實現類基本上都只是某一個 volitile 修飾的 int 型變量其保證每一個線程都能擁有對該 int 的可見性和原子修改;對於linux內核中來講,它也是利用互斥量或信號量等內存數據作標記。

  6. 對於分佈式狀況來講,咱們只要能找到一個公共標記被全部機器能夠訪問,就能夠經過這個標記來完成分佈式鎖的性質。咱們能夠把這個標記放到公共內存中,如Redis,Memcache;也能夠放在磁盤上,如數據庫中;

  7. 有了標記以後,咱們要考慮這個分佈式鎖的性質,如是否可重入,是否公平,是否阻塞;考慮這個分佈式鎖的高可用和高性能;考慮這個分佈式鎖的實現方式,如樂觀鎖,悲觀鎖等等

  8. 能夠基於數據庫的主鍵和版本號,Redis的SETNX()、EXPIRE()方法作分佈式鎖,也能夠用Zookeeper來構建分佈式鎖

集羣的發現

不少的中間件設計都會使用Zookeeper做爲集羣中組成員的發現機制,客戶端會在Zookeeper上創建一個臨時目錄,Zookeeper會和客戶端創建一條長鏈接,而且定時發送心跳,一旦發現客戶端失活,Zookeeper就會刪除當前客戶端創建的臨時節點,同時將消息發送給監聽者。Zookeeper的這種特性被不少中間件應用做爲集羣發現機制。好比kafka使用Zookeeper維護broker和消費組的狀態,Hbase使用Zookeeper選舉集羣的master,dubbo使用Zookeeper維護各個服務的實例存活狀態(這個是從網上舶來的,由於我當時沒理解面試官的意思,因此這個問題其實跳過了)

broker到consumer的消息不丟失

  1. MQ的消息流程分爲producer->broker->consumer,要保證消息不丟失,須要保證這兩個傳遞過程的可靠性。面臨的不穩定因素有網絡異常,節點宕機等等。

  2. RocketMQ採用了ack機制,若是consumer消費不到消息,broker會重複投遞這個消息(投遞次數能夠自定義),直到消費成功,這裏要保證消費接口的冪等性;同時,Consumer自身維護一個持久化的offset,標記這已經成功發回broker的下標 ,經過這個下標,即便Consumer宕機,也能夠在重啓以後到MessageQueue中拉去消息

aop的兩種實現和緣由

  1. Spring AOP使用jdk動態代理和cglib。若是被繼承者有接口的時候,使用JDK的動態代理,不然,則使用CGLIB

  2. JDK的動態代理須要實現一個公共的接口(經過接口找到代理的方法),動態代理生成的反射類。Proxy是具體的代理,咱們在實現了InvocationHandler以後的invoke方法會進入Proxy(繼承了Proxy,實現了接口)的方法中

  3. CGLib採用的是用建立一個繼承實現類的子類,用asm庫動態修改子類的代碼來實現的,因此能夠用傳入的類引用執行代理類

                                      後記
    • 1

從三輪面試狀況來看,面試官隨着級別的不一樣,問的問題也約偏架構,可是算法永遠是不變的一個點。建議你們刷刷LeetCode。

整個秋招我面試了國內不少的互聯網公司,經歷的面試大概有30+場,在接下來的日子裏,我會把這些筆記慢慢發佈出來,供你們準備春招和實習。其實大約三個月沒有面試了,上面這些問題我都有些不知道了,因此仍是告誡咱們,要持續學習!

其實後續我計劃把個人面試帖子整理成PDF,大概分爲面試步驟、常見面試題、個人面試經歷、必刷算法、OFFER選擇四個模塊。但願不會咕咕

總結了一些2020年的面試題,這份面試題的包含的模塊分爲19個模塊,分別是: Java 基礎、容器、多線程、反射、對象拷貝、Java Web 、異常、網絡、設計模式、Spring/Spring MVC、Spring Boot/Spring Cloud、Hibernate、MyBatis、RabbitMQ、Kafka、Zookeeper、MySQL、Redis、JVM 須要本資料的能夠自行領取點擊這裏~暗號博客園~。
在這裏插入圖片描述

相關文章
相關標籤/搜索