1. 程式人生 > >阿里java高階工程師面試100題

阿里java高階工程師面試100題

大型網站架構技術QQ群:368614849

1,java堆,分新生代老年代,新生代有Eden,from surviver,to surviver三個空間,堆被所有執行緒共。eden記憶體不足時,發生一次minor GC,會把from survivor和eden的物件複製到to survivor,這次的to survivor就變成了下次的from survivor,經過多次minor GC,預設15次,達到次數的物件會從survivor進行老年代。1次new如果新生代裝不下,則直接進入老年代。

2,HashMap和HashTable是使用陣列+連結串列結構實現,根據Hash和table長度計算陣列的下標index做操作,hashMap預設陣列長度為16,hashMap對null值的key都放在table[0]的位置,table[index]形成1個連結串列,當然在新版jdk中連結串列節點數>8會變成紅黑樹結構。hashMap達到最大數量會擴容,擴容table長度變為2倍,每個元素(table中)但重新計算index放到新的table中。

3,堆的年輕代和老年代。

堆的年輕代大則老年代小,GC少,但是每次時間會比較長。年輕代小則老年代大,會縮短每次GC的時間,但是次數頻繁。可以讓老年代儘量快取常用物件,JVM預設年輕代和老年代的大小比例為1:2,。觀察峰值老年代記憶體,不影響full GC,加大老年代可調1:1,但是要給老年代預留三分之一的空間。減少使用全域性變數和大物件 ,調整新生代,老年代到最合適。

4,位元組流不會用到記憶體緩衝區,檔案本身直接操作。字元流操作使用記憶體快取區,用快取存操作檔案。字元流在輸出前將所有內容暫時儲存到記憶體中,即快取區暫時儲存,如果想不關閉也將字元流輸出則可以使用flush方法強制刷出。位元組字元轉化可能存在系統編碼lang,要制定編碼。getbyte位元組流使用更加廣泛。

5,中文佔用2個位元組,read()函式讀1個位元組把A會讀入的原因。ASCII碼是8位,A在ASCII碼中有對應碼,A只要8位就能表示,但是unicode是支援ASCII碼的,在unicode中表示A是使用低8位的ASCII碼,補上高8位的0,read()1分位元組就已經讀入A的ASCII碼,列印時會給其高8位補上0,所以顯示正常A。

6,喚醒一個阻塞的執行緒

如因為Sleep,wait,join等阻塞,可以使用interrupted exception異常喚醒。

7,記憶體溢位可能原因和解決。

原因可能是A,資料載入過多,如1次從資料庫中取出過多資料   B,集合類中有對物件的引用,用完後沒有清空或者集合物件未置空導致引用存在等,是的JVM無法回收  C,死迴圈,過多重複物件 D,第三方軟體的bug       E,啟動引數記憶體值設定的過小。

例如方法:修改JVM啟動引數,加記憶體(-Xms,-Xmx);錯誤日誌,是否還有其他錯誤;程式碼走查

8,redis使用單執行緒模型,資料順序提交,redis支援主從模式,mencache只支援一致性hash做分散式;redis支援資料落地,rdb定時快照和aof實時記錄操作命令的日誌備份,memcache不支援;redis資料型別豐富,有string,hash,set,list, sort set,而memcache只支援簡單資料型別;memcache使用cas樂觀鎖做一致性。

jedis操作Hash:hmset, hmget, hdel, hkeys

jedis操作List: lpush,lrange按照範圍取出,rpush, del, sort等keyjedis操作Set:sadd,srem移除noname,smembers, sismember, scard等。

使用場景例如

Hash:儲存讀取更新使用者多個屬性

List:微博TimeLine,訊息列表

Set:共同好友,二度好友,用唯一性可以統計網站所有獨立IP,好友推薦根據tag求交集,大於threshold就可以推薦。

sortset:set增加1個權重score引數

其他場景:A訂閱釋出系統,redis對某個key訊息釋出及訂閱,當1個key訊息釋出後,所有訂閱它的客戶端都會收到相應訊息,例如實時訊息系統,即時聊天,群聊等。

事務-常用EX,EC提交執行的命令,在server不出問題,可以保證一連串的命令是順序執行額;提供1個watch功能,對1個key作watch,然後再執行transation。

9,Class.forName()將類載入到JVM,還會對類解釋,執行static塊,而ClassLoader也載入到JVM,但是不會執行static塊,並且只有呼叫了new Instance方法才會呼叫建構函式。

10,java反射機制。

可以在執行時判斷一個物件所屬的類,構造一個類的物件,判斷類具有的成員變數和方法,呼叫1個物件的方法。4個關鍵的類:Class,Constructor,Field,Method。    getConstructor獲得建構函式/getDeclardConstructor; getField/getFields/getDeclardFields獲得類所生命的所有欄位;getMethod/getMethods/getDeclardMethod獲得類宣告的所有方法,正常方法是一個類建立物件,而反射是1個物件找到1個類。

11,Object類中的方法:clone(),但是使用該方法必須實現Java.lang.Cloneable介面,equal()方法判斷引用是否一致,指向同一物件,即相等於==,只有覆寫了equals()方法之後,才可以說不同。hashcode(),物件的地址, toString(), finalize()。

12,序列化和反序列化

序列化和反序列化即物件和位元組序列間的轉化,程序間傳送文字圖片音訊等以二進位制傳送。JDK中ObjectOuputStream和ObjectInputStream為輸出輸入流,只有實現SeriaLizable/Externalizable介面的類才能被序列化。如Person物件傳遞給記憶體流使用DataConstractJsonSeralizer, MemoryStream stream = new MemoryStream(); DataConstractJsonSeralizer SER = new DataConstractJsonSeralizer(typeof(person));  ser.writeObjectStream(stream, person);顯示json輸出,StramReader sr = new StreamReader(stream1); sr.ReadToEnd()。

13,講講分散式唯一ID。

確定ID儲存用64位,1個64位二進位制1是這樣的00000000.....1100......0101,切割64位,某段二進位制表示成1個約束條件,前41位為毫秒時間,後緊接9位為IP,IP之後為自增的二進位制,記錄當前面位數相同情況下是第幾個id,如現在有10臺機器,這個id生成器生成id極限是同臺機器1ms內生成2的14次方個ID。

分散式唯一ID = 時間戳 << 41位, int型別伺服器編號 << 10,序列自增sequence。每個時間戳內只能生成固定數量如(10萬)個自增號,達到最大值則同步等待下個時間戳,自增從0開始。將毫秒數放在最高位,保證生成的ID是趨勢遞增的,每個業務線、每個機房、每個機器生成的ID都是不同的。如39bit毫秒數|4bit業務線|2bit機房|預留|7bit序列號。高位取2016年1月1日1到現在的毫秒數,系統執行10年,至少需要10年x365天x24小時x3600秒x1000毫秒=320x10~9,差不多39bit給毫秒數,每秒單機高峰併發小於100,差不多7bit給每毫秒的自增號,5年內機房小於100臺機器,預留2bit給機房,每個機房小於100臺機器,預留7bit給每個機房,業務線小於10個,預留4bit給業務線標識。

64bit分散式ID(42bit毫秒+5bit機器ID+12位自增)等

生成分散式ID的方式:A,2個自增表,步長相互隔開   B,時間的毫秒或者納秒  C,UUID         D,64位約束條件(如上)

14,NIO和IO的區別

第一點,NIO少了1次從核心空間到使用者空間的拷貝。

ByteBuffer.allocateDirect()分配的記憶體使用的是本機記憶體而不是Java堆上的記憶體,和網路或者磁碟互動都在作業系統的核心空間中發生。allocateDirect()的區別在於這塊記憶體不由java堆管理, 但仍然在同一使用者程序內。

第二點,NIO以塊處理資料,IO以流處理資料

第三點,非阻塞,NIO1個執行緒可以管理多個輸入輸出通道

15,記憶體洩漏

未對作廢資料記憶體單元置為null,儘早釋放無用物件的引用,使用臨時變數時,讓引用變數在推出活動域後自動設定為null,暗示垃圾收集器收集;程式避免用String拼接,用StringBuffer,因為每個String會佔用記憶體一塊區域;儘量少用靜態變數(全域性不會回收);不要集中建立物件尤其大物件,可以使用流操作;儘量使用物件池,不再迴圈中建立物件,優化配置;建立物件到單例getInstance中,物件無法回收被單例引用;伺服器session時間設定過長也會引起記憶體洩漏。

16,物件克隆和實現方式

克隆的物件可能包含一些已經修改過的屬性,而new1個物件屬性都還是初始化時候的值,被複制克隆的類要實現Clonable介面,覆蓋clone()方法,訪問修飾符為public,方法中呼叫super.clone()得到所需要的複製方法,類中的屬性類也需要實現Clonable介面,覆寫clone()方法,並在super中也呼叫子屬性類的clone()複製,才可以實現深拷貝。

或者寫到流中序列化的方式來實現,不必考慮引用型別中還包含引用型別,直接用序列化來實現物件的深複製拷貝,即將物件寫到流,再從流中讀出來,需要實現seriazation介面。

17,redis記憶體資料上升到一定大小會執行資料淘汰策略,redis提供了6種資料淘汰策略。

LRU:從已設定過期時間的資料集合中挑選最近最少使用的資料淘汰

random:從已設定過期時間的資料中挑選任意資料淘汰

ttl:從已設定過期時間的資料集合中挑選將要過期的資料淘汰。

notenvision:禁止驅逐資料

如mysql中有2千萬資料,redis只儲存20萬的熱門資料。LRU或者TTL都滿足熱點資料讀取較多,不太可能超時特點。

redis特點:速度塊,O(1),豐富的資料型別,支援事物原子性,可用於快取,比memecache速度塊,可以持久化資料。

常見問題和解決:Master最好不做持久化如RDB快照和AOF日誌檔案;如果資料比較重要,某分slave開啟AOF備份資料,策略為每秒1次,為了主從複製速度及穩定,MS主從在同一區域網內;主從複製不要用圖狀結構,用單向連結串列更為穩定 M-S-S-S-S。。。。;redis過期採用懶漢+定期,懶漢即get/set時候檢查key是否過期,過期則刪除key,定期遍歷每個DB,檢查制定個數個key;結合伺服器效能調節併發情況。

過期淘汰,資料寫入redis會附帶1個有效時間,這個有效時間內該資料被認為是正確的並不關心真實情況,例如對支付等業務採用版本號實現,redis中每一份資料都維持1個版本號,DB中也維持1份,只有當redis的與DB中的版本一致時,才會認為redis為有效的,不過仍然每次都要訪問DB,只需要查詢version版本欄位即可。

18,非同步化,生產介面每秒鐘10萬併發,消費者用非同步慢慢消費。快取模式空間換時間,把1兩億的資料名單打到快取。服務降級,把不重要的任務放棄;靜態資源離線包下載機制,在wify下會主動提前把靜態下載前端層保護可請將使用者請求延長,點選後主動給它隨機等待2s的時間/2分鐘之內不能請求;後端做部分介面的開關,設定超短耗時時間,原來只用5ms的只給20ms。

系統一段時間內會自動重試,重試多次後就認為是失敗了,檢查支付介面返回該訂單的錢,支付操作如果回覆錯誤則回滾扣庫存的事務,沒返回則會記錄進行中pendding狀態,結束整個過程,等通知失敗/成功,AB系統之間會出現死迴圈補償,如B退單不成功,一般就是記錄錯誤日誌了。超時每隔一段時間去定時回撥服務定時回滾,一定次數還是超時則提示使用者聯絡客服,訂單庫存可以不會滾,記錄狀態,如果一直呼叫支付不成功,則讓使用者自己去處理聯絡客服,可以不回滾使用者的資料,金額扣了才算真正完成,是一種簡單粗暴的做法。

公共配置抽象成儲存到zookeeper配置中心或者redis等,DB也儲存一份,各應用監聽ZK的配置變化,可以建一個配置web管理頁面。

19,dubbo用ProxyFactoty代理工廠將HelloServiceImpl封裝成1個Inoke執行,即ProxyFactory.getInvoke(ref, (Class)介面,註冊URL,解碼引數),並將Invoke匯出成1個Exporter,包括去註冊中心ZK註冊服務。Invoke有本地執行的Invoke,遠端通訊執行的Invoke。

20,每次扣減庫存時加上1個請求流水編號,上層請求扣減庫存沒拿到結果的話,重新查詢1次做重試操作,量不大都是加鎖處理。減少鎖的時間,犧牲冪等性,扣減為DB下地操作,查詢扣減和設定合成1步,中間沒有網路請求。利用快取,通過寫log記錄操作,非同步合併日誌及更新,重啟時cache失效,讀log恢復,避免重複提交,寫操作不建議重試快速失敗。多個商品同時增減庫存,可使用訂單號做冪等處理,應用層對單個商品減庫存,操作排隊,商品訊息ID路由在1個應用server處理,讀本地快取,失效再redis,DB採用樂觀鎖,組提交,1次減庫存多個訂單的購買量。可將同一個key下庫存m分為n組k1......kn,每組數為m/n,扣減依次在各組扣減,減少併發衝突。佇列裝滿後關閉佇列進入,然後使用者輪訓自己是否搶到了非同步ajax,使用者資源佇列固定長度。2個佇列,1個銷售的資源佇列放入redis,有另外1個佇列用來裝搶購的會員的uid。

紅包狀態正常,併成功將狀態改為“已領取”,且訊息傳送成功,使用者端開始消費該訊息,如果消費失敗/超時,用MQ做重試做冪等,直到成功,每條訊息有唯一編號且保證訊息處理成功與去重表的日誌同時出現。

熱點將hot data拆分,分在不同庫和不同表,分散熱點Data,減輕DB併發更新熱點帶來RT升高和應用連線超時。SQL在mysql層加以限制,SQL超時/thradrunning到1定值則拒絕SQL執行,一定時間非同步將結果寫入DB,nginx對IP做限制,可能誤殺。

21,SpringAOP,XML配置<aop:config>,切面<aop:aspect>切點<aop:pointcut>,連線切點和通知方法<aop:before>和<aop:after>等,註解可以直接使用@before執行方法@after ,@before(“pointcut()”) ,@after("pointcut"), @Aroud("excutete()),@AfteReturning,@AfterThrowing,可作日誌事務,許可權等待,AOP即通過把具體的類建立對應的 代理類,從代理類來對具體進行操作。                      

目標實現了介面,預設採用JDK實現AOP,也可以強制使用CGlib來實現AOP,目標沒有實現介面的話,則必須採用CGlib,Spring自動在JDK和CGlib切換。如果要求spring強制使用CGlib實現AOP,則可以配置,新增Cglib庫。。。jar, Spring配置檔案中加入<aop:aspecj-autoproxy proxy-target-Class=true>                                                                                                                                                                                   

22,MyISM採用表級鎖,對Myism表讀不會阻塞讀,會阻塞同表寫,對Myism寫則會阻塞讀和寫,即一個執行緒獲得1個表的寫鎖後,只有持有鎖的執行緒可以對錶更新操作,其他執行緒的讀和寫都會等待。

InnoDB,採用行級鎖,支援事務,例如只對a列加索引,如果update ...where a=1 and b=2其實也會鎖整個表, select 使用共享鎖,update insert delete採用排它鎖,commit會把鎖取消,當然select by id for update也可以制定排它鎖。

23,實時佇列採用雙佇列模式,生產者將行為記錄寫入Queue1,worker服務從Queue1消費新鮮資料,如果異常則寫入Queue2(主要儲存異常資料),RetryWorker會監聽Queue2,消費異常資料,如果還未處理成功按照一定的策略等待或者將異常資料再寫入Queue2,如果資料發生積壓可以調整worker的消費遊標,從最新資料重新開始消費,保證了最新data得到處理,中間未處理的一段則可以啟動backupWorker指定起止遊標在消費完指定區間的資料後,backupWorker會自動停止。

DB降級開關後,可直接寫入redis(storm),同時將資料寫入一份到Retry佇列,在開啟DB降級開關後消費Retry佇列中的資料,從而把資料寫入到mysql中,達到最終一致性。MYSQL切分為分片為2的N次方,例如原來分為兩個庫d0和d1均放在s0伺服器上,s0同時有備機s1,擴容只要幾步驟:確保s0到s1伺服器同步順利,沒有明顯延遲;s0暫時關閉讀寫許可權;確保s1已經完全同步到s0更新;s1開放讀寫許可權;d1的dns由s0切換到s1;s0開放讀寫許可權。

24,DB的特性和隔離級別

4大特性:原子性,一致性,分離性,永續性

隔離級別:

讀提交:寫事務禁止讀

讀未提交:寫事務允許讀

可重複讀:寫事務禁止讀事務,讀禁止寫

序列化:全部禁止

詳細說明:讀提交1個事務開始寫則全部禁止其他事務訪問該行。讀未提交1個事務開始寫則不允許其他事務同時寫,但可以讀。可重複讀 讀事務會禁止寫事務,寫事物則禁止其他任何事務。序列化效能最低,全部禁止,序列執行。 MYSQL預設的是可重複讀。

25,帖子服務、元資料服務、帖子搜尋服務,提供索引資料儲存,tid和uid查詢直接從帖子服務從元資料返回,其他檢索查詢有帖子搜尋服務從索引資料檢索並返回,帖子服務增刪改查用MQ同步到帖子搜尋服務,搜尋服務修改索引的資料(索引樹,倒排表),索引表t_mapping(tid,uid)。

300億資料在全量索引庫中,數百萬一天內修改過的資料在一天庫中,50萬小時內修改過的資料在小時庫中,在update請求時,只會操作最低級別的索引例如小時庫。小時庫,1小時合併一次,合併到天庫,天庫一天合併1次,合併到全量庫中。

26,講一下NIO和網路傳輸

NIO Reactor反應器模式,例如汽車是乘客訪問的實體reactor,乘客上車後到售票員處Acceptor登記,之後乘客便可休息睡覺了,到達乘客目的地後,售票員Aceptor將其喚醒即可。持久TCP長連結每個client和server之間有存在一個持久連線,當CCU(使用者併發數量)上升,阻塞server無法為每個連線執行1個執行緒,自己開發1個二進位制協議,將message壓縮至3-6倍,傳輸雙向且訊息頻率高,假設server連結了2000個client,每個client平均每分鐘傳輸1-10個message,1個messaged的大小為幾百位元組/幾千位元組,而server也要向client廣播其他玩家的當前資訊,需要高速處理訊息的能力。Buffer,網路位元組存放傳輸的地方,從channel中讀寫,從buffer作為中間儲存格式,channel是網路連線與buffer間資料通道,像之前的socket的stream。

27,快取擊透

預載入;

載入DB時同步,其他則等待;

DB端做SQL合併,Queue合併排隊處理;

部分快取設定為永不過期;

先清除快取,讀取資料時候則等待500ms,500ms快取應該已經載入完成;

採用雙key快取,A1為原始快取,A2為拷貝快取;

如果DB為空null則g給redis設定1個NFC空nei容。

28,Dubbo原始碼使用了哪些設計模式

A,工廠模式,ExtenstionLoader.getExtenstionLoader(Protocol.class).getAdaptiveExtenstion()

B,裝飾器模式+責任鏈,以provider的呼叫鏈為例,具體呼叫鏈程式碼是在protocolFilterWrapper的buildInvokeChain完成的,將註解中含有group=provider的Filter實現,呼叫順序為EchoFilter -> ClassLoaderFilter -> GenericFilter -> ContextFilter -> ExceptionFilter -> TimeoutFilter -> MonitorFilter -> TraceFilter。裝飾器模式和責任鏈混合使用,Echo是回聲測試請求,ClassLoaderFilter則只是在其主功能上添加了功能。

C,觀察者模式,provider啟動時需要與註冊中心互動,先註冊自己的服務,再訂閱自己的服務,訂閱時採用了觀察者模式,註冊中心每5s定時檢查是否有服務更新,有更新則向服務提供者傳送1個notify訊息後即可執行NotifyListener的notity方法,執行監聽器方法。

D,動態代理模式。  擴充套件JDK的ExtensionLoaderdeAdaptive實現,根據呼叫階段動態引數決定呼叫哪個類,生成代理類的程式碼是ExtensionLoader的createAdaptiveExtenstionClassLoader方法。

29,平衡二叉樹,左右高度之差不超過1,Add/delete可能造成高度>1,此時要旋轉,維持平衡狀態,避免二叉樹退化為連結串列,讓Add/Delete時間複雜度但控制在O(log2N),旋轉演算法2個方法,1是求樹的高度,2是求2個高度最大值,1個空樹高度為-1,只有1個根節點的樹的高度為0,以後每一層+1,平衡樹任意節點最多有2個兒子,因此高度不平衡時,此節點的2棵子樹高度差為2。例如單旋轉,雙旋轉,插入等。

紅黑樹放棄完全平衡,追求大致平衡,保證每次插入最多要3次旋轉就能平衡。

30,多執行緒同步鎖

A,RentrantLock,可重入的互斥鎖,可中斷可限時,公平鎖,必須在finally釋放鎖,而synchronize由JVM釋放。可重入但是要重複退出,普通的lock()不能響應中斷,lock.lockInterruptbly()可響應中斷,可以限時tryLock(),超時返回false,不會永久等待構成死鎖。

B,Confition條件變數,signal喚醒其中1個在等待的執行緒,signalall喚醒所有在等待的執行緒await()等待並釋放鎖,與lock結合使用。

C,semaphore訊號量,多個執行緒比(額度=10)進入臨界區,其他則阻塞在臨界區外。

D,ReadWriteLock,讀讀不互斥,讀寫互斥,寫寫互斥。

E,CountDownLantch倒數計時器,countdown()和await()

F,CyCliBarrier

G,LockSupport,方法park和unpark

31,棧溢位的原因

是否遞迴的呼叫;大量迴圈;全域性變數是否過多;陣列,List,Map資料是否過大;用DDMS工具檢查地方。

記憶體溢位的原因

過多使用了static;static最好只用int和string等基本型別;大量的遞迴或者死迴圈;大資料項的查詢,如返回表的所有記錄,應該採用分頁查詢。檢查是否有陣列、List、map中存放的是物件的引用而不是物件,這些引用會讓對應物件不能被釋放。

棧過大會導致記憶體佔用過多,頻繁頁交換阻礙效率。

32,說一下http/2

Http/2採用二進位制格式而不是文字

Http/2是完全多路複用的,而非有序並阻塞的。

Http/2使用報頭壓縮

Http/2讓伺服器可以將響應主動推送到客戶端快取中。

33,說一下記憶體洩露

A,HashMap,vector等容易(靜態集合類), 和應用程式生命週期一樣,所引用的所有物件Object也不能釋放。

B,當集合類裡面的物件屬性被修改後,再呼叫remove()不起作用,hashcode值發生了改變

C,其物件add監聽器,但是往往釋放物件時忘記去刪除這些監聽器

D,各種連線記得關閉

E,內部類的引用

F,呼叫其他模組,物件作用引數

G,單例模式,持有外部物件引用無法收回。

記憶體洩露例子

Vector<String> A = new Vector<String>();

for(int i = 0; i < 100; i++){

Object o = new Object ();

A.add(o);

o = null;

}

 ........

記憶體溢位的例子

StringBuffer b = new StringBuffer ();

for(int i =0; i < 100; i++){

for(int j =0; i < 100; j++){

b.append(*);

}

}

34,SpirngMVC的生命週期 和 SpringBean的生命週期

SpirngMVC的生命週期 :

A,DispatcherSerlvet(前端控制器)

B,-》 HandlerMapping(處理器對映器),根據xml註解查詢對應的Hander -》 返回Handler

C,-》處理器介面卡去執行Handler

D,-》Handler執行完成後給處理器介面卡返回ModelAndView

E,-》前端控制器請求檢視解析器去執行檢視解析,根據邏輯檢視名解析成真正的檢視JSP,向前端控制器返回view

F,-》前端控制器進行檢視渲染,將模型資料放到request-》返回給使用者

SpringBean的生命週期:

Instance例項化-》設定屬性值-》呼叫BeanNameAware的setBeanName方法-》呼叫BeanPostProsessor的預初始化方法-》呼叫InitializationBean的afterPropertiesSet()的方法-》呼叫定製的初始化方法callCustom的init-method-》呼叫BeanPostProsessor的後初始化方法-》Bean可以使用了 -》 容器關閉-》 呼叫DisposableBean的destroy方法-》呼叫定製的銷燬方法CallCustom的destroy-method。

35,AQS,抽象佇列同步器

AQS定義2種資源共享方式:獨佔與share共享

獨佔:只能有1個執行緒執行

share共享:多個執行緒可以同p執行如samphore/countdownlanch

AQS負責獲取共享state的入隊和/喚醒出隊等,AQS在頂層已經實現好了,AQS有幾種方法:acquire()是獨佔模式下執行緒共享資源的頂層入口,如獲取到資源,執行緒直接返回,否則進入等待佇列,直到獲取到資源為止。tryAcquire()將執行緒加入等待佇列的尾部,並標誌為獨佔。acquireQueued()使執行緒在等待佇列中獲取資源,一直到獲取資源後不返回,如果過程被中斷也返回true,否則false。

執行緒在等待過程中被中斷是不響應的,獲取資源才補上中斷。將執行緒新增到佇列尾部用了CAS自旋(死迴圈直到成功),類似於AutomicInteger的CAS自旋volatile變數。

start->tryAcquire -> 入隊 -> 找安全點 -> park等待狀態 -> 當前節點成對頭 -> End

36,單例模式的7種寫法

懶漢2種,列舉,餓漢2種,靜態內部類,雙重校驗鎖(推薦)。

37,lucence倒排索引

三個檔案:字典檔案,頻率檔案,位置檔案。詞典檔案不僅儲存有每個關鍵詞,還保留了指向頻率檔案和位置檔案的指標,通過指標可以找到該關鍵字的頻率資訊和位置資訊。

field的概念,用於表達資訊所在位置(如標題中,文章中,url中),在建索引中,該field資訊也記錄在詞典檔案中,每個關鍵詞都有一個field資訊(因為每個關鍵字一定屬於一個或多個field)。

關鍵字是按字元順序排列的(lucene沒有使用B樹結構),因此lucene可以用二元搜尋演算法快速定位關鍵詞

假設要查詢單詞 “live”,lucene先對詞典二元查詢、找到該詞,通過指向頻率檔案的指標讀出所有文章號,然後返回結果。詞典通常非常小,因而,整個過程的時間是毫秒級的。   

對詞典檔案中的關鍵詞進行了壓縮,關鍵詞壓縮為<字首長度,字尾>,例如:當前詞為“阿拉伯語”,上一個詞為“阿拉伯”,那麼“阿拉伯語”壓縮為<3,語>。對數字的壓縮,數字只儲存與上一個值的差值。

38,ZooKeeper分散式高可用

ZooKeeper 執行期間,叢集中至少有過半的機器儲存了最新資料。叢集超過半數的機器能夠正常工作,叢集就能夠對外提供服務。

zookeeper可以選出N臺機器作主機,它可以實現M:N的備份;keepalive只能選出1臺機器作主機,所以keepalive只能實現M:1的備份。

通常有以下兩種部署方案:雙機房部署(一個穩定性更好、裝置更可靠的機房,這個機房就是主要機房,而另外一個機房則更加廉價一些,例如,對於一個由 7 臺機器組成的 ZooKeeper 叢集,通常在主要機房中部署 4 臺機器,剩下的 3 臺機器部署到另外一個機房中);三機房部署(無論哪個機房發生了故障,剩下兩個機房的機器數量都超過半數。在三個機房中都部署若干個機器來組成一個 ZooKeeper 叢集。假設機器總數為 N,各機房機器數:N1 = (N-1)/2 ,N2=1~(N-N1)/2 ,N3 = N - N1 - N2 )。

水平擴容就是向叢集中新增更多機器,Zookeeper2種方式(不完美),一種是叢集整體重啟,另外一種是逐臺進行伺服器的重啟。
 

39,如何將資料分佈在redis第幾個庫?

答:redis 本身支援16個數據庫,通過 資料庫id 設定,預設為0。
例如jedis客戶端設定。一:JedisPool(org.apache.commons.pool.impl.GenericObjectPool.Config poolConfig, String host, int port, int timeout, String password, int database);
第一種通過指定建構函式database欄位選擇庫,不設定則預設0庫。二:jedis.select(index);呼叫jedis的select方法指定。

40,類載入器的雙親委派載入機制?

答:當一個類收到了類載入請求,他首先不會嘗試自己去載入這個類,而是把這個請求委派給父類去完成,每一個層次類載入器都是如此,因此所有的載入請求都應該傳送到啟動類載入其中,只有當父類載入器反饋自己無法完成這個請求的時候(在它的載入路徑下沒有找到所需載入的Class),子類載入器才會嘗試自己去載入。

41,kafka高效能的原因?

答:

A,Broker NIO非同步訊息處理,實現了IO執行緒與業務執行緒分離;

B,磁碟順序寫;

C, 零拷貝(跳過使用者緩衝區的拷貝,建立一個磁碟空間和記憶體的直接對映,資料不再複製到使用者態緩衝區);

D,分割槽/分段(每次檔案操作都是對一個小檔案的操作,非常輕便,同時也增加了並行處理能力);

F,批量傳送 (可以指定快取的訊息達到某個量的時候就發出去,或者快取了固定的時間後就傳送出去,大大減少服務端的I/O次數)

E,資料壓縮

42,冪等的處理方式?

答:一、查詢與刪除操作是天然冪等

二、唯一索引,防止新增髒資料

三、token機制,防止頁面重複提交

四、悲觀鎖  for update

五、樂觀鎖(通過版本號/時間戳實現, 通過條件限制where avai_amount-#subAmount# >= 0)

六、分散式鎖

七、狀態機冪等(如果狀態機已經處於下一個狀態,這時候來了一個上一個狀態的變更,理論上是不能夠變更的,這樣的話,保證了有限狀態機的冪等。)

八、select + insert(併發不高的後臺系統,或者一些任務JOB,為了支援冪等,支援重複執行)

43,HTTPS工作流程?
a、客戶端傳送自己支援的加密規則給伺服器,代表告訴伺服器要進行連線了
b、伺服器從中選出一套加密演算法和hash演算法以及自己的身份資訊(地址等)以證書的形式傳送給瀏覽器,證書中包含伺服器資訊,加密公鑰,證書的辦法機構
c、客戶端收到網站的證書之後要做下面的事情: 
        c1、驗證證書的合法性
        c2、如果驗證通過證書,瀏覽器會生成一串隨機數作為金鑰K,並用證書中的公鑰進行加密
        c3、用約定好的hash演算法計算握手訊息,然後用生成的金鑰K進行加密,然後一起傳送給伺服器
d、伺服器接收到客戶端傳送來的資訊,要求下面的事情: 
       d1、用私鑰解析出密碼,用密碼解析握手訊息,驗證hash值是否和瀏覽器發來的一致
       d2、使用金鑰加密訊息,回送
如果計演算法hash值一致,握手成功
 

44,RabbitMQ訊息堆積怎麼處理?

答:

  • 增加消費者的處理能力(例如優化程式碼),或減少釋出頻率
  • 單純升級硬體不是辦法,只能起到一時的作用
  • 考慮使用佇列最大長度限制,RabbitMQ 3.1支援
  • 給訊息設定年齡,超時就丟棄
  • 預設情況下,rabbitmq消費者為單執行緒序列消費,設定併發消費兩個關鍵屬性concurrentConsumers和prefetchCount,concurrentConsumers設定的是對每個listener在初始化的時候設定的併發消費者的個數,prefetchCount是每次一次性從broker裡面取的待消費的訊息的個數
  • 建立新的queue,消費者同時訂閱新舊queue
  • 生產者端快取資料,在mq被消費完後再發送到mq
  • 打破發送迴圈條件,設定合適的qos值,當qos值被用光,而新的ack沒有被mq接收時,就可以跳出傳送迴圈,去接收新的訊息;消費者主動block接收程序,消費者感受到接收訊息過快時主動block,利用block和unblock方法調節接收速率,當接收執行緒被block時,跳出傳送迴圈。

45,RabbitMQ的訊息丟失解決方案?

答:

  • 訊息持久化:Exchange 設定持久化:durable:true;Queue 設定持久化;Message持久化傳送。
  • ACK確認機制:訊息傳送確認;訊息接收確認。

46,負載均衡演算法?

常見6種負載均衡演算法:輪詢,隨機,源地址雜湊,加權輪詢,加權隨機,最小連線數。

nginx5種負載均衡演算法:輪詢,weight,ip_hash,fair(響應時間),url_hash

dubbo負載均衡演算法:隨機,輪詢,最少活躍呼叫數,一致性Hash

47,JVM記憶體區域劃分?

答:

  • 堆:Java中的堆是用來儲存物件本身的以及陣列(當然,陣列引用是存放在Java棧中的),是Java垃圾收集器管理的主要區域。堆是被所有執行緒共享的,在JVM中只有一個堆。
  • 虛擬機器棧:虛擬機器棧中存放的是一個個的棧幀,每個棧幀對應一個被呼叫的方法,在棧幀中包括區域性變量表、運算元棧、指向當前方法所屬的類的執行時常量池的引用、方法返回地址和一些額外的附加資訊。當執行緒執行一個方法時,就會隨之建立一個對應的棧幀,並將建立的棧幀壓棧。當方法執行完畢之後,便會將棧幀出棧。
  • 本地方法棧:本地方法棧則是為執行本地方法(Native Method)服務的,在HotSopt虛擬機器中直接就把本地方法棧和Java棧合二為一
  • 方法區:方法區與堆一樣,是被執行緒共享的區域。方法區儲存了類的資訊(包括類的名稱、方法資訊、欄位資訊)、靜態變數、常量以及編譯器編譯後的程式碼等。在方法區中有一個非常重要的部分就是執行時常量池,它是每一個類或介面的常量池的執行時表示形式,在類和介面被載入到JVM後,對應的執行時常量池就被創建出來。當然並非Class檔案常量池中的內容才能進入執行時常量池,在執行期間也可將新的常量放入執行時常量池中,比如String的intern方法。當方法區無法滿足記憶體分配需求時,則丟擲OutOfMemoryError異常。在HotSpot虛擬機器中,用永久代來實現方法區,將GC分代收集擴充套件至方法區,但是這樣容易遇到記憶體溢位的問題。JDK1.7中,已經把放在永久代的字串常量池移到堆中。JDK1.8撤銷永久代,引入元空間。
  • 程式計數器(執行緒私有):是當前執行緒所執行的位元組碼的行號指示器,每條執行緒都要有一個獨立的程式計數器,這類記憶體也稱為“執行緒私有”的記憶體。正在執行java方法的話,計數器記錄的是虛擬機器位元組碼指令的地址(當前指令的地址)。如果還是Native方法,則為空。
  • 直接記憶體:在JDK1.4中新加入的NOI類,引入了一種基於通道與緩衝區的I/O方式,它可以使用Native函式直接分配堆外記憶體,然後通過一個儲存在Java堆中的DirectByteBuffer物件作為這塊記憶體的引用進行操作。

48,jvm YGC和FGC發生的具體場景?

答:

正在處理的實現事務功能,下次自動回滾。

佇列實現持久化儲存,下次啟動自動載入。

新增標誌位,未處理 0,處理中 1,已處理 2。每次啟動的時候,把所有狀態為 1 的,置為 0。

關鍵性的應用就給電腦配個 UPS。

YGC :對新生代堆進行gc。頻率比較高,因為大部分物件的存活壽命較短,在新生代裡被回收。效能耗費較小。
FGC :全堆範圍的gc。預設堆空間使用到達80%(可調整)的時候會觸發fgc。以我們生產環境為例,一般比較少會觸發fgc,有時10天或一週左右會有一次。

YGC發生場景:edn空間不足
FGC發生場景:old空間不足,perm空間不足,呼叫方法System.gc() ,ygc時的悲觀策略, dump live的記憶體資訊時(jmap –dump:live)

49,一個執行緒池正在處理服務如果忽然斷電該怎麼辦?

答:

佇列實現持久化儲存,下次啟動自動載入。
但是實際需要看情況,大體思路是這樣。
新增標誌位,未處理 0,處理中 1,已處理 2。每次啟動的時候,把所有狀態為 1 的,置為 0。或者定時器處理
關鍵性的應用就給電腦配個 UPS。

50,SpringBoot的優點?

答:

快速構建專案,極大的提高了開發、部署效率。
對主流開發框架的無配置整合。
專案可獨立執行,無須外部依賴Servlet容器。
提供執行時的應用監控。

51,DoS,DDoS,DRDoS攻擊分別是什麼?

答:DoS是Denial of Service的簡寫就是拒絕服務。

DDoS就是Distributed Denial of Service的簡寫就是分散式拒絕服務。

DRDoS就是Distributed Reflection Denial of Service的簡寫,分佈反射式拒絕服務。

52,服務限流的方式?

答:

  • 漏桶:水(請求)先進入到漏桶裡,漏桶以一定的速度出水(介面有響應速率),當水流入速度過大會直接溢位(訪問頻率超過介面響應速率),然後就拒絕請求。
  • 令牌桶演算法:系統會按恆定1/QPS時間間隔(如果QPS=100,則間隔是10ms)往桶裡加入Token,如果桶已經滿了就不再加了.新請求來臨時,會各自拿走一個Token,如果沒有Token就拒絕服務。
  • 基於redis實現的限流:假設每分鐘訪問次數不能超過10次,在Redis中建立一個鍵,過期60秒,對此服務介面的訪問就把鍵值加1,在60秒內增加到10的時候,禁止訪問服務介面。
  • 計數器,滑動視窗

53,Quartz實現原理?

答:A、scheduler是一個計劃排程器容器(總部),容器裡面可以盛放眾多的JobDetail和trigger,當容器啟動後,裡面的每個JobDetail都會根據trigger按部就班自動去執行。
B、JobDetail是一個可執行的工作,它本身可能是有狀態的。
C、Trigger代表一個排程引數的配置,什麼時候去調。
D、當JobDetail和Trigger在scheduler容器上註冊後,形成了裝配好的作業(JobDetail和Trigger所組成的一對兒),就可以伴隨容器啟動而排程執行了。
E、scheduler是個容器,容器中有一個執行緒池,用來並行排程執行每個作業,這樣可以提高容器效率。

54,資料庫的鎖?

答:行鎖(共享鎖和排他鎖),表鎖,頁級鎖,頁級鎖,意向鎖,讀鎖,寫鎖,悲觀鎖,樂觀鎖等

55,簡述ThreadPoolExecutor內部工作原理?

答:

先檢視當前執行狀態,如果不是RUNNING 狀態會拒絕執行任務,如果是RUNNING狀態,就會檢視當前執行的執行緒數量,如果小於核心執行緒數,會建立新的執行緒來執行這個任務,如果不小於核心執行緒,會將這個任務放到阻塞佇列去等代執行,直到上一個任務執行完再來執行這個任務。如果失敗會建立一個非核心執行緒來執行這個任務如果當前執行緒數大於最大執行緒數,會直接拒絕該任務。

56,聚集索引和非聚集索引的區別?

答:

聚集索引:
索引中鍵值的邏輯順序決定了表中相應行的物理順序(索引中的資料物理存放地址和索引的順序是一致的),可以這麼理解:只要是索引是連續的,那麼資料在儲存介質上的儲存位置也是連續的。
比方說:想要到字典上查詢一個字,我們可以根據字典前面的拼音找到該字,注意拼音的排列時有順序的。
聚集索引就像我們根據拼音的順序查字典一樣,可以大大的提高效率。在經常搜尋一定範圍的值時,通過索引找到第一條資料,根據實體地址連續儲存的特點,然後檢索相鄰的資料,直到到達條件截至項。
非聚集索引
索引的邏輯順序與磁碟上的物理儲存順序不同。非聚集索引的鍵值在邏輯上也是連續的,但是表中的資料在儲存介質上的物理順序是不一致的,即記錄的邏輯順序和實際儲存的物理順序沒有任何聯絡。索引的記錄節點有一個數據指標指向真正的資料儲存位置。
總結如下:
如果一個主鍵被定義了,那麼這個主鍵就是作為聚集索引
如果沒有主鍵被定義,那麼該表的第一個唯一非空索引被作為聚集索引
如果沒有主鍵也沒有合適的唯一索引,那麼innodb內部會生成一個隱藏的主鍵作為聚集索引,這個隱藏的主鍵是一個6個位元組的列,改列的值會隨著資料的插入自增。
InnoDB引擎會為每張表都加一個聚集索引,而聚集索引指向的的資料又是以物理磁碟順序來儲存的,自增的主鍵會把資料自動向後插入,避免了插入過程中的聚集索引排序問題。如果對聚集索引進行排序,這會帶來磁碟IO效能損耗是非常大的。

57,java併發包下有哪些類?

答:ConcurrentHashMap,ConcurrentSkipListMap,ConcurrentNavigableMap

CopyOnWriteArrayList

BlockingQueue,BlockingDeque (ArrayBlockingQueue,LinkedBlockingDeque,LinkedBlockingQueue,DelayQueue,PriorityBlockingQueue,SynchronousQueue)

ConcurrentLinkedDeque,ConcurrentLinkedQueue,TransferQueue,LinkedTransferQueue

CopyOnWriteArraySet,ConcurrentSkipListSet

CyclicBarrier,CountDownLatch

Lock(ReetrantLock,ReetrantReadWriteLock)

Atomic包

58,threadlocal為什麼會出現oom?

答:ThreadLocal裡面使用了一個存在弱引用的map, map的型別是ThreadLocal.ThreadLocalMap. Map中的key為一個threadlocal例項。這個Map的確使用了弱引用,不過弱引用只是針對key。每個key都弱引用指向threadlocal。 當把threadlocal例項置為null以後,沒有任何強引用指向threadlocal例項,所以threadlocal將會被gc回收。 
但是,我們的value卻不能回收,而這塊value永遠不會被訪問到了,所以存在著記憶體洩露。因為存在一條從current thread連線過來的強引用。只有當前thread結束以後,current thread就不會存在棧中,強引用斷開,Current Thread、Map value將全部被GC回收。最好的做法是將呼叫threadlocal的remove方法。

在ThreadLocal的get(),set(),remove()的時候都會清除執行緒ThreadLocalMap裡所有key為null的value,但是這些被動的預防措施並不能保證不會記憶體洩漏:

(1)使用static的ThreadLocal,延長了ThreadLocal的生命週期,可能導致記憶體洩漏。 
(2)分配使用了ThreadLocal又不再呼叫get(),set(),remove()方法,那麼就會導致記憶體洩漏,因為這塊記憶體一直存在。

59,mysql資料庫鎖表怎麼解決?

答:查詢鎖表資訊
當前執行的所有事務
select * from information_schema.innodb_trx
當前出現的鎖
select * from information_schema.innodb_locks
鎖等待的對應關係
select * from information_schema.innodb_lock_waits  

通過 select * from information_schema.innodb_trx 查詢 trx_mysql_thread_id然後執行 kill 執行緒ID
KILL   8807;//後面的數字即時程序的ID

60,java 判斷物件是否是某個類的型別方法?

  • instanceof 運算子是用來在執行時指出物件是否是特定類的一個例項。instanceof通過返回一個布林值來指出,這個物件是否是這個特定類或者是它的子類的一個例項。
  • getClass判斷,如o.getClass().equals(ClassA.class)。(使用instanceof來判斷一個物件是不是屬於某個類,但是有時候這個類是繼承於一個父類的,所以,不能嚴格判斷出是不是自己的類,而不是自己的父類。) 

61,Spring+MyBatis實現讀寫分離簡述?

答:

  • 方案一:通過MyBatis配置檔案建立讀寫分離兩個DataSource,每個SqlSessionFactoryBean物件的mapperLocations屬性制定兩個讀寫資料來源的配置檔案。將所有讀的操作配置在讀檔案中,所有寫的操作配置在寫檔案中。
  • 方案二:通過Spring AOP在業務層實現讀寫分離,在DAO層呼叫前定義切面,利用Spring的AbstractRoutingDataSource解決多資料來源的問題,實現動態選擇資料來源
  • 方案三:通過Mybatis的Plugin在業務層實現資料庫讀寫分離,在MyBatis建立Statement物件前通過攔截器選擇真正的資料來源,在攔截器中根據方法名稱不同(select、update、insert、delete)選擇資料來源。
  • 方案四:通過spring的AbstractRoutingDataSource和mybatis Plugin攔截器實現非常友好的讀寫分離,原有程式碼不需要任何改變。推薦第四種方案

62,紅黑樹的特點?

答:(1)每個節點或者是黑色,或者是紅色。
(2)根節點是黑色。
(3)每個葉子節點(NIL)是黑色。 [注意:這裡葉子節點,是指為空(NIL或NULL)的葉子節點!]
(4)如果一個節點是紅色的,則它的子節點必須是黑色的。
(5)從一個節點到該節點的子孫節點的所有路徑上包含相同數目的黑節點。[這裡指到葉子節點的路徑]