當你開始開始去跳槽面試的時候,明明只是一份15K的作業,卻問你會不會多執行緒,懂不懂高并發,火箭造得讓你猝及不防,結果就是涼涼;現如今市場,多執行緒、高并發編程、分布式、負載均衡、集群等可以說是現在高級后端開發求職的必備技能,
關于Java基礎,Spring,多執行緒、高并發編程、分布式、負載均衡、集群等這些資料我都整理好了,需要的自行領取點擊這里~暗號博客園~,
百度一面1
String/StringBuffer和StringBuilder的區別
-
String是由final修飾的類,同時它是由byte(9+)或者char(8-)陣列組成的,這些陣列也是final的
-
StringBuffer是執行緒安全的,StringBuilder是執行緒不安全的,StringBuffer是通過synchronized的方法級別來實作的
-
對于StringBuffer和StringBuilder來說,他們有一個共同的父類,即AbstractStringBuilder,他們的屬性和類都沒有final修飾,所以導致了他們是可變的,相對來說,StringBuffer有自己的cache,保證了查詢的性能,這個cache在builder中是沒有的
介面和抽象類
-
在JDK5之前,介面和抽象類在語法層面上有著顯著的區別:介面不能有自己的方法體,同時介面只能是public的;抽象類可以有自己實作的方法,同時抽象類也可以有空方法
-
隨著JDK的升級,在Java8時,介面可以有default方法,到了Java9之后,介面也可以有自己的私有方法,介面除了屬性默認是public final的之外,幾乎和抽象類在語法層面,沒有任何區別
-
所以對于這兩者的區別,我們要站在更高的的角度,從設計的層面去看它們之間的區別,在我看來介面的設計是自上而下的,而抽象類的設計是自下而上的,設計模式中的模板方法模式,就是用抽象類的一個較好的體現,
鎖
-
Java中的鎖其實分為兩大類,一個是悲觀鎖,一個是樂觀鎖,悲觀鎖指的是synchronized家族的,使用的時候包括Object#notify()、Object#notifyAll()和Object#wait(),樂觀鎖指的是由CAS包延伸出來的一系列鎖,包括J.U.C中的ReentrantLock、ReentrantReadWriteLock,與之配合使用的是Condition
-
對于Synchronized來說,它的鎖粒度是物件級別的,默認是Class物件,也可以是我們指定的實體物件,當修飾方法的時候會在位元組碼的flags中表明為ACC_SYNCHRONIZED標識,該標識指明了該方法是一個同步方法,JVM 通過該 ACC_SYNCHRONIZED 訪問標志來辨別一個方法是否宣告為同步方法,如果有設定,則需要先獲得監視器鎖,當修飾代碼塊時,會在位元組碼中通過 monitorenter 和 monitorexit 執行來進行加鎖,當執行緒執行到 monitorenter 的時候要先獲得所鎖,才能執行后面的方法,Synchronized隨著JDK的演變做出了一系列優化,如輕量級鎖,鎖粗化,鎖消除,自旋鎖等等,
-
對于ReentrantLock來說,是一種可重入鎖,它對應兩個內部類分別表示公平鎖和非公平鎖,都繼承自Sync,而Sync繼承自AQS,于公平鎖的tryAcquire()來說,它比非公平鎖多了一個!hasQueuedPredecessors()的判斷,即檢查如果當前執行緒位于佇列的最前面或佇列為空,才會讓它獲得鎖
-
相對于Synchronized,ReentrantLock需要手動獲取釋放,支持公平鎖,選擇性通知等等功能,
大概說下集合,hashMap的并發問題,HashMap中key為NULL時存的位置
-
Java中的集合分為兩部分,一個是java.util包下的集合,包括list,vector,map,set等,還有一個就是J.U.C包下的并發集合
-
對于list來說,分為陣列存盤和鏈表存盤,分別是ArrayList和LinkedList(陣列存盤中還有vector,不過其效率較低一般不用),對于Map來說,分為HashMap,WeakHashMap、TreeMap和LinkedHashMap,對于HashMap來說,它通過拉鏈法來解決hash沖突的問題;對于TreeMap來說,他通過紅黑樹來對key進行排序;對于LinkedHashMap來說,它通過鏈表記錄了每個key插入的順序,可以通過它實作LRU演算法;對于WeakHashMap來說,它類似于ThreadLocal中的Entry,都繼承了WeakReference,當key不被參考的時候,可以下次GC的時候洗掉;對于Set來說,它的幾個派生類都是由對應Map實作的
-
HashMap在1.7版本時,當Entry陣列多執行緒擴容時,因為使用的是頭插法,會導致出現一個回圈的單鏈表,導致get死回圈的問題(1.8時已經修復),除此之外,并發put元素時有可能導致覆寫問題,
-
HashMap中key為null,hash的結果是0,它會被保存在entry[0]上
NIO和BIO
-
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模型來說,一旦事件回應體很大,那么就會導致后續的事件遲遲得不到處理,并且會影響新的事件輪詢
-
Java目前為止支持3種IO,分別是BIO,NIO和AIO,BIO對應OS的阻塞式IO,NIO對應著OS中的多路復用模型,AIO則是異步IO模型,
-
阻塞式IO模型 BIO:當用戶執行緒發出IO請求之后,內核會去查看資料是否就緒,如果沒有就緒就會等待資料就緒,而用戶執行緒就會處于阻塞狀態,用戶執行緒交出CPU,當資料就緒之后,內核會將資料拷貝到用戶執行緒,并回傳結果給用戶執行緒,用戶執行緒才解除block狀態,如socket.accept()
執行緒狀態及轉換
-
執行緒共有六種基本狀態,分別是NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED
-
RUNNABLE:分為運行態和就緒態,就緒態即在等待一些資源如CPU
-
BLOCKED:等待monitor的鎖,一般是要進入synchronized塊或者方法中的執行緒,這對應著synchronized中物件的MonitorObject的_EntryList佇列
-
WAITING:當執行緒呼叫了Object.wait(),Thread.join()或者LockSupport.park()方法時,進入等待狀態,直到使用notify()/notifyAll(),LockSupport.unpark()或者呼叫方法的執行緒結束
-
TIMED_WAITING:是一種特殊的等待狀態,當執行緒呼叫了Thread.sleep(long),Object.wait(long), Thread.join(long), LockSupport.parkNanos(long)或者LockSupport.parkUntil(long)方法時,進入超時等待狀態
-
TERMINATED:執行緒已經完成執行,進入結束狀態
-
NEW:執行緒被new出來,但是沒有呼叫start方法
執行緒池
-
執行緒池是一種池化技術,降低資源消耗,提高回應速度,提高現成的可管理性
-
執行緒池有七個引數,分別是corePoolSize,maxmumPoolSize,keepAliveTime,unit,workQueue,threadFactory,handle,當任務來臨時,執行緒池的執行緒數會逐漸增大到core,然后把多余的放到佇列中,如果超過佇列長度,則增加執行緒池數目至max,如果還繼續增加,則通過handle進行拒絕,當執行緒空閑time時間后,會銷毀到core的數目,
-
J.U.C提供四種常見的執行緒池,分別是fixed(執行固定執行緒數目),single(執行單個任務),cache(執行多個短期任務)和scheduled(執行周期性任務),前兩個等待佇列無限長,后兩個最大執行緒數無限大
快取一致性
-
只要有快取,就存在快取一致性問題
-
先更新資料庫,再更新快取的話:如果A更新資料庫,接著B更新資料庫,接著B更新快取,接著A更新快取,造成了資料庫和快取不一致的情況;同樣,先更新快取,后更新資料庫也有一樣的問題
-
先洗掉快取,再更新資料庫:如果A洗掉了快取,接著B去查資料庫,B把臟值填入快取中,A再更新資料庫,此時快取已經有值了,導致快取和資料庫不一致
-
采用延時雙刪的方案,即先刪除快取,再更新資料庫,然后延時再洗掉快取,當快取洗掉失敗的時候,可以將失敗的key發送至訊息佇列,重新消費
有序陣列的合并
百度二面1
資料庫索引
-
對于MySQL來說,其索引是B+樹的結構,二叉搜索樹保證了索引是有序的,平衡二叉樹保證了索引不會退化到鏈表的極端情況,B樹降低了平衡二叉樹的嚴格性,提高了構建效率,B+樹將值放到葉子節點中,是的樹變矮,提高IO效率
-
除此B+樹之外,還有Hash索引,全文索引,R-Tree索引,可以通過B+Tree索引創建業務性質的自適應Hash索引(這個Innodb也有一定的優化,其中騰訊在11月份還貢獻了點優化的代碼,詳情可以查看騰訊技術工程公眾號#一個即將寫入MySQL原始碼的官方bug解決之路)
-
索引還有一些概念如:聚簇索引,組合索引,主鍵索引,索引下推,唯一索引,最左原則,覆寫索引 ,前綴索引
-
一些索引失效的場景包括:like違背最左原則,型別不匹配,使用!等
AQS
-
AQS通過內置的FIFO雙端雙向鏈表來完成獲取資源執行緒的排隊作業,雙端雙向鏈表,該佇列由一個一個的Node結點組成,每個Node結點維護一個prev參考和next參考,分別指向自己的前驅和后繼結點,AQS維護兩個指標,分別指向佇列頭部head和尾部tail,該佇列中的Node有五種狀態,分別是CANCELLED,SIGNAL, CONDITION,PROPAGATE和初始狀態
-
從使用上來說,AQS的功能可以分為兩種:獨占(如ReentrantLock)和共享(如Semaphore/CountDownLatch,CyclicBarrier/ReadWriteLock),ReentrantReadWriteLock可以看成是組合式,它對讀共享,寫獨占
-
除了ReentrantLock外,還有三種常用的AQS組件,分別是Semaphore,CountDownLatch和CyclicBarrier
欄位太長為什么不去建立索引
-
欄位過長的話會使得索引的存盤變得很大,同時查找起來也會降低效率
-
所以我們不如使用前綴索引來盡可能降低索引的長度
-
寫一個LRU,并發的LRU該怎么寫
百度三面
-
1
-
分布式鎖
-
對于單機的鎖來說,它只是一個所有執行緒/行程都能看到的一個標記而已,對于Java來說,synchronized是在物件頭設定標記,Lock 介面的實作類基本上都只是某一個 volitile 修飾的 int 型變數其保證每個執行緒都能擁有對該 int 的可見性和原子修改;對于linux內核中來說,它也是利用互斥量或信號量等記憶體資料做標記,
-
對于分布式情況來說,我們只要能找到一個公共標記被所有機器可以訪問,就可以通過這個標記來完成分布式鎖的性質,我們可以把這個標記放到公共記憶體中,如Redis,Memcache;也可以放在磁盤上,如資料庫中;
-
有了標記之后,我們要考慮這個分布式鎖的性質,如是否可重入,是否公平,是否阻塞;考慮這個分布式鎖的高可用和高性能;考慮這個分布式鎖的實作方式,如樂觀鎖,悲觀鎖等等
-
可以基于資料庫的主鍵和版本號,Redis的SETNX()、EXPIRE()方法做分布式鎖,也可以用Zookeeper來構建分布式鎖
集群的發現
很多的中間件設計都會使用Zookeeper作為集群中組成員的發現機制,客戶端會在Zookeeper上建立一個臨時目錄,Zookeeper會和客戶端建立一條長連接,并且定時發送心跳,一旦發現客戶端失活,Zookeeper就會洗掉當前客戶端建立的臨時節點,同時將訊息發送給監聽者,Zookeeper的這種特性被很多中間件應用作為集群發現機制,比如kafka使用Zookeeper維護broker和消費組的狀態,Hbase使用Zookeeper選舉集群的master,dubbo使用Zookeeper維護各個服務的實體存活狀態(這個是從網上舶來的,因為我當時沒理解面試官的意思,所以這個問題其實跳過了)
broker到consumer的訊息不丟失
-
MQ的訊息流程分為producer->broker->consumer,要保證訊息不丟失,需要保證這兩個傳遞程序的可靠性,面臨的不穩定因素有網路例外,節點宕機等等,
-
RocketMQ采用了ack機制,如果consumer消費不到訊息,broker會重復投遞這個訊息(投遞次數可以自定義),直到消費成功,這里要保證消費介面的冪等性;同時,Consumer自身維護一個持久化的offset,標記這已經成功發回broker的下標 ,通過這個下標,即使Consumer宕機,也可以在重啟之后到MessageQueue中拉去訊息
aop的兩種實作和原因
-
Spring AOP使用jdk動態代理和cglib,如果被繼承者有介面的時候,使用JDK的動態代理,否則,則使用CGLIB
-
JDK的動態代理需要實作一個公共的介面(通過介面找到代理的方法),動態代理生成的反射類,Proxy是具體的代理,我們在實作了InvocationHandler之后的invoke方法會進入Proxy(繼承了Proxy,實作了介面)的方法中
-
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 需要本資料的可以自行領取點擊這里~暗號博客園~,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/238389.html
標籤:其他
上一篇:程式人生:暗網如何影響你的安全性
下一篇:我的2020技術總結
