1.java基礎
1.1 說一說java有哪些集合
答:分層次記憶:第一層:Collection;第二層:List、Set、Queue;第三層:Vector、ArrayList、Deque、PriorityQueue、HashSet、SortedSet、EnumSet;第四層:Stack、LinkedList、ArrayQueue、LinkedHashSet、TreeSet;
集合主要還是指collection下面的接口,如果問的是容器的話還需要回答map相關內容;
同樣用分層記憶:第一層:Map;第二層:IdentityHashMap、SortedMap、WeakHashMap、HashTable、EnumMap;第三層:LinkedHashMap、Properties、TreeMap;
1.2 說一說ArrayList跟LinkedList的區別;
答:ArrayList底層結構是數組,隨機查詢效率較高,增刪元素慢;
LinkedList底層結構是鏈表,隨機查詢效率比較慢,但是增刪元素快;
1.3 說一下HashMap實現原理
答:1.jdk1.8之前是數組加上鏈表,1.8以后是數組加上鏈表加上紅黑樹;
2.默認初始化容量為16,且每次擴容為2的n次冪,負載因子為0.75,鏈表閾值為8;
3.每次擴容后這個值只可能在兩個地方,一個是原下標的位置,另一種是在下標為 <原下標+原容量> 的位置;
4.加入紅黑樹的原因是因為hash碰撞過多產生的鏈表過長會導致查詢效率降低,轉變紅黑樹以后查詢效率更改,查詢時間復雜度在O(logN);
5.hashCode計算方法為,i.高 16bit 不變,低 16bit 和高 16bit 做了一個異或;
6.hashMap是線程不安全的容器,情況1:在put的時候,當hashcode一樣時,會覆蓋值;情況2:并發擴容的時候也會存在同時擴容,最終只留下了最后一次擴容的數據,導致數據丟失;
7.當容量大于容量*0.75時,put后進行擴容;
1.4 說一說volatile關鍵字的原理
答:1.volatile用來標明屬性內存可見性,即程序每一個獲取到的volatile的標量都是最新的;
2.volatile還可以防止指令重排序;
3.voltile修飾后會在操作系統層面,對變量的存取加內存屏障,使其每次獲取值都是從主存拿;
1.5 說一下ConcurrentHashMap
答:1.這是一個線程安全的hashMap容器,采用分段鎖實現;
2.jdk1.8之前用的是segment加鎖來實現,1.8之后使用volatile+CAS來實現,降低了復雜度以及提供了性能;
3.get操作時,沒有加任何鎖,因為Node的成員val是用volatile修飾的;
4.put時,使用鎖和cas實現線程安全;
5.內部node上加volatile修飾是為了保證擴容時對其他線程可見;
1.6 說一說java I/O
答:IO 總的來說分為兩個階段:第一階段是等待數據到達內核緩沖區,第二階段是將數據從內核緩沖區復制到用戶緩沖區。
1.javaI/O氛圍BIO、NIO、AIO;
2.傳統形式的InputStream等類為BIO;
3.NIO主要涉及Channel,Buffers,Selectors ;代表框架為netty;
4.AIO在兩個階段都完成以后才發送信號,數據是直接可用的;
1.7 java鎖機制
答:這里主要說兩個類,Synchronized和ReentrantLock;
1.Synchronized是一個關鍵字,只要給加上這個關鍵字,jvm底層會幫我們做同步
如果加在靜態方法上,那么鎖對象為該類;
如果加在普通方法上,那么鎖對象為該實例;
如果是靜態同步塊,那么鎖對象為括號里的對象;
加上該關鍵字后,jvm字節碼會多出兩個指令,一個monitorEnter和monitorExit;理解為獲取鎖和釋放鎖;
2.ReentrantLock是一個類,我們可以通過new 來獲取鎖對象,用這個類來控制鎖的獲取和釋放更加靈活;
每次在同步塊之前通過tryLock()方法嘗試獲取鎖,但是必須在退出同步方法中自己手動釋放鎖,否則下一個線程永遠都獲取不到鎖,導致程序崩;
3.jvm底層有無鎖,偏向鎖,輕量級鎖,重量級鎖的概念,且鎖只能升級不能降級;鎖標志是放在java對象的頭里Mark word,獲取到鎖后,會在mark word里面記錄當前線程的id;
1.8 Thread.sleep()跟object.wait()的區別;
答:sleep方法只是阻塞了線程,不釋放鎖;而wait方法調用后不僅阻塞線程,還釋放鎖并且需要等到notify喚醒;調用后線程都是進入阻塞狀態;
1.9 公平鎖與非公平鎖底層實現原理
答:公平鎖是指按照線程等待順序獲取鎖,這種方式會造成性能低下,大量的時間花費在線程調度上;
非公平鎖是指不管等待順序,隨機獲取;
都是基于AQS實現,內部維護一個雙向鏈表,表結點Node的值就是每一個請求當前鎖的線程,公平鎖每次從隊首取,非公平鎖不去判斷隊列直接搶占,如果獲取不到就放入隊列,后續就一個個來獲取鎖了;
具體底層使用volatile 修飾的state字段和CAS操作來實現;
1.10 紅黑樹特點
答:1.每個節點不是紅色就是黑色
2.根節點必須為黑色
3.紅色節點的葉節點只能是黑色
4.從任意節點到它的每個葉子結點的所有路徑都包含相同的黑色結點
1.11 java8的新特性
答:1.函數式編程,新增lambda表達式
2.Opntional類
3.時間相關類,例如LocalDate等
4.jvm變更,取消了PermGen,新增了元數據區,這個區域跟本地內存共享
5.接口允許添加默認方法
1.12 一個類的字節碼由哪些部分組成
答:1.魔術,表示文件類型,防止篡改
2.jdk版本號
3.常量池相關信息,如容量,類型,字面量符號引用,類型,結構等
4.訪問標志
5.索引、父類索引、接口索引
6.字段表,方發表,屬性表
1.13 對用引用類型
答:1.強引用:直接new ,可以顯示地將引用賦值為null,這樣一來的話,JVM在合適的時間就會回收該對象。
2.軟引用:SoftReference ,只有在內存不足的時候JVM才會回收該對象,適合用來做緩存
3.弱引用:WeakReference,當JVM進行垃圾回收時,無論內存是否充足,都會回收被弱引用關聯的對象
4.虛引用:PhantomReference,如果一個對象與虛引用關聯,則跟沒有引用與之關聯一樣,在任何時候都可能被垃圾回收器回收
2.spring相關
2.1 什么是IOC
答:ioc就是控制反轉的意思,將bean的管理權交給spring管理,就不需要自己new,也叫依賴注入,將每個bean所以依賴的對象交由spring框架來自動注入;
2.2 什么是AOP
答:1.aop就是面向切面編程,通過代理的方式實現,主要用來做統一處理,例如登錄攔截,日志記錄,日志埋點等操作;事務實現其實也是通過aop來的;
2.aop有多種通知,分別為前置,后置,環繞,異常等;切點可以用相關表達式去匹配,也可以自己去定義注解實現;
3.動態代理又分jdk動態代理和cglib動態代理,當有實現接口時默認使用jdk,沒有實現接口時使用cglib;
2.3 spring bean的初始化過程
答:創建上下文環境(beanFactory)-->準備容器--->加載xml內容---
->初始化bean到BeanDefinition-->實例化bean--->設置屬性---->對于實現了相關Aware接口的調用方法-->調用BeanPostProcess的postProcessBeforeInitialization方法---->調用InitializingBean的afterPropertiesSet方法--->調用指定的初始化方法----->調用BeanPostProcess的postProcessAfterInitialization方法--->bean準備就緒------->調用Dispostblebean的destory方法----->調用指定的銷毀方法
整個圖片就是:
這里可以擴展下spring循環依賴的實現,由于先實例化bean然后再設置屬性,由此解決了循環依賴的問題,spring會將實例化后的bean放入一個標記容器里面,等到設置屬性的時候就會到相關容器里面去取;由此引出spring使用三級緩存來解決該問題;
三級緩存分別為:singletonObjects(一級)指單例對象的cache,singletonFactories(三級)指單例對象工廠的cache,earlySingletonObjects(二級)指提前曝光的單例對象的cache;
spring在實例化的時候會將還未初始化完全的對象放入三級緩存中;且這里用ObjectFactory來生產對象;
例如:我們在編寫代碼時,兩個service類有時候會互相引用,但是程序運行時卻沒有報錯;
但是使用構造方法實例化的和多態的bean還是存在循環依賴問題的;
2.4 BeanFactory跟applicationContext的區別
答:1.beanFactory面向spring,而applicationContext面向spring開發者;
2.applicationContext擴展了beanFactory,實現了更多的功能,例如:支持aop,web等插件,國際化,事件傳遞等;
2.5 SpringMVC的流程圖
答:2.6 Spring boot的優點
答:1.簡化配置,不需要xml文件
2.自動配置,許多的插件只需要引入相關依賴包,并配置上相關參數就可以使用
3.應用監控強大,提供一系列端點可以監控服務及應用,做健康檢測。
4.內置了tomcat容器,部署起來方便
2.6 Spring boot初始化流程
答:1.SpringApplication的main方法調用run時,主要做三件事:
根據classpath下是否存在(ConfigurableWebApplicationContext)判斷是否要啟動一個web applicationContext。
SpringFactoriesInstances加載classpath下所有可用的ApplicationContextInitializer
SpringFactoriesInstances加載classpath下所有可用的ApplicationListener
2.遍歷初始化過程中加載的SpringApplicationRunListeners,然后調用starting(),開始監聽springApplication的啟動
3.加載SpringBoot配置環境(ConfigurableEnvironment)
4.banner配置
5.ConfigurableApplicationContext(應用配置上下文)創建,根據webEnvironment是否是web環境創建默認的contextClass,通過BeanUtils實例化上下文對象,并返回
6.prepareContext()方法將listeners、environment、applicationArguments、banner等重要組件與上下文對象關聯。
7.refreshContext(context),bean的實例化完成IoC容器可用的最后一道工序
3. mybatis相關
3.1 mybatis有幾級緩存,分別是
答:mybatis分二級緩存,分別sqlsession和namespace,默認開啟一級緩存
3.2 mybatis“$“和“#”的區別
答:“$“不做預編譯,“#”會進行預編譯,可以有效防止sql注入問題;
4.數據庫相關
4.1 mysql事務的隔離級別
答:讀未提交,讀已提交,不可重復讀,序列化,mysql默認級別為不可重復讀;
4.2 什么是臟讀,什么是幻讀,什么是不可重復讀;
答:1.臟讀:臟讀即為事務1第二次讀取時,讀到了事務2未提交的數據。若事務2回滾,則事務1第二次讀取時,讀到了臟數據
2.幻讀:事務1第二次查詢時,讀到了事務2提交的數據。
2.不可重復讀:不可重復讀與臟讀邏輯類似。主要在于事務2在事務1第二次讀取時,提交了數據。導致事務1前后兩次讀取的數據不一致。
4.3 事務的特性
答:acid分別為原子性,一致性,隔離性,持久性;
4.4 事務的傳播行為
答:PROPAGATION_REQUIRED 支持當前事務,假設當前沒有事務。就新建一個事務
PROPAGATION_SUPPORTS 支持當前事務,假設當前沒有事務,就以非事務方式運行
PROPAGATION_MANDATORY 支持當前事務,假設當前沒有事務,就拋出異常
PROPAGATION_REQUIRES_NEW 新建事務,假設當前存在事務。把當前事務掛起
PROPAGATION_NOT_SUPPORTED 以非事務方式運行操作。假設當前存在事務,就把當前事務掛起
PROPAGATION_NEVER 以非事務方式運行,假設當前存在事務,則拋出異常
PROPAGATION_NESTED 如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則執行與PROPAGATION_REQUIRED類似的操作。
4.5 如何優化sql
答:1.首先查看是否是聯表查詢,如果是就進行語句拆分;
2.拆分以后如果還是慢,就進行sql語句分析利用explain語句;
3.如果發現沒有使用索引,那么就修改語句使走索引,如果沒有,那就考慮是否需要建立;
這里需要注意:聯合索引的最左匹配原則;
4.6 哪些情況下不會走索引
答:1.or語句,如果or前后有一個不走索引就不會走索引;
2.like匹配,只有前綴匹配才走索引;
3.等號前面進行運算
4.用聯合索引時,沒有使用第一個索引字段
5.如果列類型是字符串則必須用引號,否則不走索引
6.如果存在隱式轉換也是不走索引的
4.7 說一下數據庫的數據結構
答:這里拿innodb來說明:
1.B+tree的數據結構,為什么不用Btree是因為B+tree的每一層能放下更多地址,只索引到下一個節點,不保存數據,這樣的話就降低了樹的高度,減少了查詢時的磁盤io;
B+tree的特點:
非葉子節點只存儲鍵值信息。
所有葉子節點之間都有一個鏈指針。
數據記錄都存放在葉子節點中。
這里也可以引申說明下:當我們使用其余索引查詢時,如果只需要查詢索引數據最好就只寫該字段值,可以減少回表操作,提高性能;
4.8 說一下數據庫的mvcc
答:即多版本并發控制,在并發訪問的時候,數據存在版本的概念,可以有效地提升數據庫并發能力,讀寫不沖突,利用快照實現讀;通過保存數據在某個時間點的快照來實現的。這意味著一個事務無論運行多長時間,在同一個事務里能夠看到數據一致的視圖。根據事務開始的時間不同,同時也意味著在同一個時刻不同事務看到的相同表里的數據可能是不同的。
innodb實現:在每一行數據中額外保存兩個隱藏的列:當前行創建時的版本號和刪除時的版本號。這里的版本號并不是實際的時間值,而是系統版本號。每開始新的事務,系統版本號都會自動遞增。事務開始時刻的系統版本號會作為事務的版本號,用來和查詢每行記錄的版本號進行比較。
4.9 mysql explain type類型
答:1.all:全表掃描
2.index:另一種形式的全表掃描,只是掃描順序根據索引順序
3.range:有范圍的索引掃描,優于index
4.ref:使用了索引,但該索引列的值并不唯一,有重復
5.ref_eq:唯一查詢,只有一個
5. 線程池相關
5.1 如何new線程池
答:一般使用ThreadPoolExecute來創建線程池,參數詳解:
corePoolSize:核心可運行線程數
maximumPoolSize:最大可運行運行程數
workQueue:阻塞隊列
keepAliveTime:當線程大于核心線程數時,且阻塞隊列沒有元素,最大等待時間
threadFactory:生成線程的工廠類
handler:超出線程池最大承受能力之后的失敗策略方法對象
5.2 為什么不用靜態方法創建
答:1.new CachedThreadPool,new ScheduledThreadPool線程池無限大,當任務過大時會導致oom;
2.new FixedThreadPool,new SingleThreadpool,當請求隊列過大時,會導致oom;
5.3 線程池submit和execute方法的區別
答:submit有返回值,execute沒有返回值,當我們需要方法運行后返回值來做處理時應該使用submit;
5.4 線程的生命周期
答:1.新建(new Thread())
2.就緒(Runnable)調用 start啟動
3.運行(Running)
4.堵塞(blocked)sleep、wait、join、suspend 都會阻塞;
5.死亡(dead)
6.jvm相關
6.1 說一下jvm內存模型;
答:jvm分為堆、棧、程序計數器、本地方法棧、方法區、元數據區(這里需要說下jdk1.8以前和以后的區別),堆又分年輕代,幸存區,和老年代;
可以擴展說明下:年輕代使用復制算法,老年代是用標記-整理;
6.2 說下jvm是如何標記存存活對象
答:1.可達性分析法:通過將一些稱為”GC Roots”的對象作為起始點,從這些節點開始搜索,搜索和該節點發生直接或者間接引用關系的對象,將這些對象以鏈的形式組合起來,形成一張“關系網”,又叫做引用鏈
2.引用計數法:就是給對象添加一個引用計數器,每當有一個地方引用它時就加1,引用失效時就減1,當計數器為0的時候就標記為可回收
6.3例舉jvm的垃圾收集器
答:1.Serial 復制算法
2.Serial old 標記整理
3.PerNew 復制算法
4.parallel nScavenge 復制算法
5.parallel old 標記整理
再主要說明下cms和G1
1.G1 基本不用配置,低停頓,用于大容量的堆。但是他犧牲了應用程序的吞吐量和部分堆空間。
2.CMS 配置比較復雜,合理的低停頓,用于中等或更小的堆。
3.所以當你覺得配置 CMS 太難了,或你的堆在 2 G 以上,或你想要顯式的指定停頓時間那么你可以使用 G1。否則使用 CMS
關于jvm調用還是要具體看你的目標是吞吐量還是程序停頓時間;
6.4 常用jvm的相關工具
答:Jstatus,JStack,Jmap等
6.5 什么是雙親委派模型
答:當需要加載一個類的時候,子類加載器并不會馬上去加載,而是依次去請求父類加載器加載,一直往上請求到最高類加載器:啟動類加載器。當啟動類加載器加載不了的時候,依次往下讓子類加載器進行加載。當達到最底下的時候,如果還是加載不到該類,就會出現ClassNotFound的情況。(自己不去加載交由父類去加載,一直往上,如果頂層加載不到然后依次往下)
6.6 哪些對象能作為GC root
答:1.由系統類加載器加載的對象;
2.Thread,活著的線程;
3.java方法的local變量;
4.JNI方法的local變量;
5.JNI全局引用;
6.用于JVM特殊目的由GC保留的對象;
7.用于同步的監控對象;
6.7 虛擬機棧使用是否會內存溢出
答:內存溢出分為多種情況:
1.堆棧溢出
2.PermGen的溢出
3.使用ByteBuffer中的allocateDirect方法的,沒有做clear,jvm垃圾回收不會回收這部分的內存;
4.堆設置過大,導致機器內存分配線程不夠;
5.地址空間不夠;
所以虛擬機棧使用會內存溢出;
7.redis相關
7.1 redis的兩種持久化機制
答:1.aof,和rdb
2.rdb適合大規模數據恢復,會存在數據丟失情況, 如果對數據一致性要求不高可以選擇這種
3.aof數據完整性更高,但是數據文件比較大,運行效率上比較慢
7.2 redis單線程為什么快
答:1.完全基于內存
2.數據結構簡單
3.采用單線程,避免了不必要的上下文切換
4.使用多路I/O復用模型
5.使用底層模型不同,Redis直接自己構建了VM 機制
7.3 什么是緩存穿透,什么是緩存擊穿,什么是緩存雪崩
答:1.緩存穿透:緩存穿透是指緩存和數據庫中都沒有的數據,而用戶不斷發起請求,如發起為id為“-1”的數據或id為特別大不存在的數據。這時的用戶很可能是攻擊者,攻擊會導致數據庫壓力過大。
- 緩存擊穿是指緩存中沒有但數據庫中有的數據(一般是緩存時間到期),這時由于并發用戶特別多,同時讀緩存沒讀到數據,又同時去數據庫去取數據,引起數據庫壓力瞬間增大,造成過大壓力
3.緩存雪崩是指緩存中數據大批量到過期時間,而查詢數據量巨大,引起數據庫壓力過大甚至down機。和緩存擊穿不同的是,緩存擊穿指并發查同一條數據,緩存雪崩是不同數據都過期了,很多數據都查不到從而查數據庫。
8. dubbo相關
8.1說一下dubbo跟java的spi機制
答:java:java spi機制的優點是解耦,但是缺點也有,比如說,不能指定獲取哪個實現類,寫在配置里面的都會被加載進來。 ServiceLoader
dubbo:支持指定實現類,支持切面,支持依賴注入 ExtensionLoader、@Adaptive標記適配器類、@SPI("值")使用默認的實現類
9.mq相關
9.1 說一下mq使用場景
答:解耦、異步、削峰
9.2 mq對比
特性 | ActiveMQ | RabbitMQ | RocketMQ | Kafka |
---|---|---|---|---|
單機吞吐量 | 萬級,比 RocketMQ、Kafka 低一個數量級 | 同 ActiveMQ | 10 萬級,支撐高吞吐 | 10 萬級,高吞吐,一般配合大數據類的系統來進行實時數據計算、日志采集等場景 |
topic 數量對吞吐量的影響 | - | - | topic 可以達到幾百/幾千的級別,吞吐量會有較小幅度的下降,這是 RocketMQ 的一大優勢,在同等機器下,可以支撐大量的 topic | topic 從幾十到幾百個時候,吞吐量會大幅度下降,在同等機器下,Kafka 盡量保證 topic 數量不要過多,如果要支撐大規模的 topic,需要增加更多的機器資源 |
時效性 | ms 級 | 微秒級,這是 RabbitMQ 的一大特點,延遲最低 | ms 級 | 延遲在 ms 級以內 |
可用性 | 高,基于主從架構實現高可用 | 同 ActiveMQ | 非常高,分布式架構 | 非常高,分布式,一個數據多個副本,少數機器宕機,不會丟失數據,不會導致不可用 |
消息可靠性 | 有較低的概率丟失數據 | - | 經過參數優化配置,可以做到 0 丟失 | 同 RocketMQ |
功能支持 | MQ 領域的功能極其完備 | 基于 erlang 開發,并發能力很強,性能極好,延時很低 | MQ 功能較為完善,還是分布式的,擴展性好 | 功能較為簡單,主要支持簡單的 MQ 功能,在大數據領域的實時計算以及日志采集被大規模使用 |
9.3 kafka offset相關
答:kafka offset分為兩種,一種是Current offset,保存在消費者端,決定調用poll方法時,拉取消息的順序,
保證收到的消息不重復;
第二種是Commited offset,保存在broker,通過commitSync和commitAsync方法來操作,是用于Consumer Rebalance過程的,能夠保證新的Consumer能夠從正確的位置開始消費一個partition,從而避免重復消費。
10.其他
10.1 如何保證接口冪等性
答:使用唯一索引和token機制
10.2 分布式優缺點
答:優點:1.便于開發和擴展;
2.每個服務足夠內聚,降低耦合度;
3.提高代碼復用性;
4.增加功能只需要增加子項目;
缺點:1.提高了系統復雜度
2.部署多個服務比較復雜;
3.因為服務多導致運維變得復雜;
4.架構復雜導致學習慢;
5.測試和查錯復雜度上升;
10.2 什么是線程安全;
答:多個線程執行同一段代碼跟單線程執行同一段代碼結果一樣;主要是通過鎖機制來解決;
10.3 分布式事務的原則
答:柔性事務:遵循CAP理論或者其變種BASE理論的事務。分布式事務基本上都是柔性事務。具有弱一致性,也就是最終一致性
10.4 CopyOnWriteArrayList實現原理
答:讀寫分離的思想,copy出原來的數據然后做操作,最終把引用指向新的容器,最終一致性;
10.5 RocketMQ是如何實現分布式事務的
答:通過事務消息來實現,發到broker的消息并不能馬上消息,稱為半消息,需要producer進行二次確認,如果二次確認途中丟失,有回查機制來解決;
10.6 讀寫鎖實現原理
答:與傳統鎖的區別是讀讀不互斥,讀寫互斥,寫寫互斥,核心類為Sync,基于AQS的實現,可以用2個int變量來實現;AQS中的stata字段高16位是讀鎖個數,低16位是寫鎖個數;
10.7 如何實現分布式鎖
答:分布式鎖實現主要有三種方式
分布式鎖實現方式 | 數據庫實現 | redis實現 | zookeeper實現 |
---|---|---|---|
實現原理 | 1.通過給數據庫記錄加樂觀鎖或者悲觀鎖;2.通過數據庫記錄,獲取鎖insert一條記錄,釋放鎖刪除記錄 | 1.通過setnx方法實現,然后設置過期時間;2.與1一樣,但是key的值為當前時間+上過期的時間 | 利用zookeeper的臨時有序節點,所有線程都去創建臨時節點,但是序列最小的獲取到鎖,釋放只要刪除節點就可以 |
優點 | 1.悲觀鎖是一種比較安全的實現;2.樂觀鎖性能高于悲觀鎖,不容易出現死鎖;3.增加記錄實現方式簡單 | 1.性能高,實現比較方便 | 1.可靠性高 |
缺點 | 1.悲觀鎖性能低,容易出現死鎖;2.樂觀鎖只能對一張表的數據加記錄,需要操作多張表是辦不到的;3.不具備可重入性,沒有鎖失效機制,不具備阻塞鎖特性 | 1.鎖超時機制不是很可靠,如果處理時間過長就會導致鎖實現;2.主從環境下可能存在key同步問題 | 1.性能比不上redis緩存鎖,因為需要頻繁的創建臨時節點 |
10.8 什么是無鎖編程
答:無鎖編程就是指利用CAS操作和volatile關鍵字實現線程安全;
10.9 什么是模塊化
答:就是講復雜系統拆分出一個個的模塊,模塊開發就是封裝細節,提供接口;重點關注內聚度以及耦合度
優點:1.可維護性高
2.靈活架構,焦點分離
3.方便模塊間組合
4.多人協作,互不干擾
5.方便單個模塊的調試和測試
缺點:
1.系統分層,調用鏈長
2.模塊間發送消息,損耗性能
10.10 秒殺需要考慮的點
答:1.獨立部署,防止影響其他業務
2.靜態化相關商品描述信息;
3.增大秒殺帶寬;
4.動態隨機成下單url,可以說獲取token,然后下單
5.限流,控制少量請求進入
3.23 新增
11 面試題
11.1 HTTP、TCP、UDP的區別
答:1.osi 七層中的層級不同,http就屬于應用層,tcp與udp是屬于傳輸層;
2.http基于tcp
3.http長連接、短連接;就是tcp長連接、短連接
4.tcp面向連接,udp不是,如三次握手,四次揮手;
11.2 websocket和tcp的關系
答:1.跟http一樣,都是基于tcp;
11.3 jdk動態代理與cglib的區別,為什么要基于接口
答:1.jdk動態代理只能對實現了接口的類做代理,而cglib沒有這個限制;
基于接口是因為:
1.生成的代理類繼承了Proxy,由于java是單繼承,所以只能實現接口,通過接口實現
2.從代理模式的設計來說,充分利用了java的多態特性,也符合基于接口編碼的規范
11.4 分布式事務
答:1.指事務的每個操作步驟都位于不同的節點,需要保證事務的acid特性;
產生原因:是分布式服務中,分庫分表;
應用場景:下單:減少庫存以及更新訂單狀態;支付:買家和商家同時扣款:前提:兩個賬戶都不在一個庫或者一個表;
解決方案:
1.兩階段提交協議
使用XA來實現,XA分為事務管理器和本地資源管理器,本地資源管理一般由數據庫實現
2.使用消息中間件
11.5 ThreadLocalMap原理
答:ThreadLocalMap其實是線程自身的一個成員屬性threadLocals的類型,內部實現跟普通的map不一樣,內部用了一個ertry數組,該數組繼承自WeakReference,WeakReference(弱引用)的特性:大家都知道WeakReference(弱引用)的特性,只要從根集出發的引用中沒有有效引用指向該對象,則該對象就可以被回收,但是這里的有效引用并不包含WeakReference,所以弱引用不影響對象被GC。WeakReference被ThreadLocal自身強引用,也就是說ThreadLocal自身的回收不受ThreadLocalMap的這個弱引用的影響;
3.24 面試題新增
12 面試題
12.1 TCP連接為什么可靠
答:1.超時重傳:發送后啟動一個定時器,等待目的確認收到,如果不及時確認,就會重發
2.給數據包編號,接收方對數據包排序,有序傳給應用層;
3.校驗:保持首部和數據的校驗和,如果校驗有錯誤就丟棄;
4.TCP接收端會丟棄重復數據;
5.擁塞控制,當網絡擁塞時,減少數據的發送;
12.2 CGLIB是如何實現動態代理的
答:通過asm字節碼處理框架來實現的一個code生產類庫;
12.3 http 和https的區別,以及https是如何保證安全的;
答: 1.http是明文傳輸,https是加密傳輸
2.https加了一層ssl/tls協議
3.https需要使用ca證書,且收費;
https加密過程:1.服務器將公鑰給瀏覽器 -->2.瀏覽器拿到公鑰之后,生成一個“會話密鑰"--->3.用公鑰加密這個“會話密鑰”發送給服務器--->4.數據傳輸的過程中,就用這個會話密鑰來加密數據
12.4 進程和線程的區別
答:根本區別:進程是操作系統資源分配的基本單位,而線程是任務調度和執行的基本單位
在開銷方面:進程切換上下文開銷大;線程可以看做輕量級的線程,開銷小;
所處環境:在操作系統中運行多個進程,在一個進程中運行多個線程,
內存分配:系統會給進程分配內存;對于線程只會分配cpu;
2020.03.25面試題新增
13 面試題
13.1 如何判斷連接是活的
答:1.應用程序自我探測;2.應用程序的探測 ;3.TCP 協議層的保活探測
13.2 數據庫死鎖
答:1.以固定的順序訪問表和行。即按順序申請鎖,這樣就不會造成互相等待的場面。
2.大事務拆小。大事務更傾向于死鎖,如果業務允許,將大事務拆小
3.在同一個事務中,盡可能做到一次鎖定所需要的所有資源,減少死鎖概率。
4.降低隔離級別。如果業務允許,將隔離級別調低也是較好的選擇,比如將隔離級別從RR調整為RC,可以避免掉很多因為gap鎖造成的死鎖。
5.為表添加合理的索引。如果不走索引將會為表的每一行記錄添加上鎖,死鎖的概率大大增大。
6.通過添加鎖,集群就分布式鎖。
13.3 java內存模型
答:簡稱JMM,分為主內存和線程內存,共享變量存放在主內存;
13.4 什么是zero copy
答:減少了內核空間和用戶空間的copy,用sendfile
13.5 如何判斷連接存活
答:使用netstat 命令;
14 2020.04.15更新
14.1 rabbitmq消息持久化的三個必要條件
答:1.消息隊列持久化。(設置durable)
2.exchange持久化。(設置durable)
3.消息持久化。(deliveryMode為2)
14.2 Spring 父子容器訪問關系
答:springMVC可以訪問Spring容器的bean,但是Spring容器不能訪問SpringMVC容器的bean。
14.3 Servlet什么時候初始化
答:看web.xml里面的配置,loadestartup值為正數即容器啟動時候初始化,為負數則第一次請求時初始化;
14.4 cloud如何自己實現負責均衡算法
答:1.集成AbstractLoadBalanceRule類;
2.注意:這個自定義的類不能放在@ComponentScan所掃描的當前包以及子包下,否則我們自定義的這個配置類就會被所有的Ribbon客戶端所共享,也就是我們達不到特殊化指定的目的了。
3.使用方式:@RibbonClient(name = "MICROSERVICECLOUD-DEPT", configuration = MySelfRule.class)
14.5 aio、nio與bio中的阻塞點
答:首先一次io分2個階段:
1.等待數據準備
是否阻塞指的就是這一個階段。
2.將數據從內核拷貝到進程中
是否同步指的就是這一個階段。
bio 兩個階段都阻塞
nio第一階段不阻塞,第二階段阻塞,通過輪詢數據是否準備好
aio兩個階段都不阻塞,由內核完成,完成后通過信號告知
屬性\模型 | 阻塞BIO | 非阻塞NIO | 異步AIO |
---|---|---|---|
blocking | 阻塞并同步 | 非阻塞但同步 | 非阻塞并異步 |
線程數(server:client) | 1:1 | 1:N | 0:N |
復雜度 | 簡單 | 較復雜 | 復雜 |
吞吐量 | 低 | 高 | 高 |
場景 | 適用于連接數目比較小且固定的架構 | 連接數目多且連接比較短(輕操作)的架構,比如聊天服務器 | 連接數目多且連接比較長(重操作)的架構,比如相冊服務器 |
14.6 linux如何查看端口占用情況
答:lsof,netstat命令;
15. 2020.04.21 面試提更新
15.1 jvm GC問題
答:GC 分為young gc、major jc、full gc;
1.young gc(minor gc) 主要針對新生代;
2.majorgc 主要針對老生代;
3.full gc是針對整個堆;
以上三種gc都會出現stw,且任何垃圾回收器都無法避免stw,jvm調優就是為了是stw的時間縮短;
出發full gc的條件:
1.代碼中顯示調用system.gc(),調用后不一定馬上觸發,需要jvm自行決定;
- young gc 之前判斷老年代可用的連續空間是否大于新生代的所有對象總空間;
①.如果大于,直接進行young gc;
②.如果小于,判斷是否開啟HandlerPromotionFailure,沒有開啟就直接full gc;
③.如果小于,且開啟該參數,判斷是否大于歷次晉升的平均值,如果小于直接full gc,大于就young gc;
3.如果創建大對象時,老年代不足以存放;
4.當持久代(方法區)空間不足,即元數據區;
15.2 官方rocketmq 跟開源 rocketmq的區別
答:
功能 | 阿里rocketmq | 開源rocketmq |
---|---|---|
安全防護 | 支持 | 不支持 |
主子賬號 | 支持 | 不支持 |
可靠性 | 高,超三分數據副本 | 不高 |
可用性 | 非常好 | 好 |
橫向擴展能力 | 支持平滑擴展,百萬級qps | 支持 |
low latency | 支持 | 不支持 |
定時消息 | 支持(精確到秒) | 支持(只支持18個level) |
事務消息 | 支持 | 不支持 |
全鏈路消息軌跡 | 支持 | 不支持 |
消息堆積能力 | 百億級別 不影響性能 | 百億級別 影響性能 |
性能(萬級topic場景) | 非常好,百萬級qps | 非常,十萬級qps |
15.3 自定義實現lru算法
答:直接編寫類繼承LinkedHashMap類,構造方法時候傳入true,表示根據最近訪問時間排列,即設置accessOrder這個字段,重寫removeEldestEntry方法,也就是制定刪除策略;
如下就是,容量超過最大值時,會刪除最少訪問的數據;
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest)
{
return size() > MAX_ELEMENTS;
}
15.4 后臺刷新緩存,前臺短時間內大量請求穿透到db;
答:通過熔斷器以及限流技術,防止大量請求穿透到db,以至于db崩掉;
15.5 為什么String是不可變得
答:
1..不可變對象可以提高String Pool的效率和安全性,直接復制地址,不需要復制數據
2.不可變對象對于多線程是安全的,修改不可變對象會報錯;
3.字符串常量池的需要,常量池中存在時就不會創建新對象;
4.允許String對象緩存HashCode,不可變就不需要重新計算hashcode;
5.安全性,用戶名,密碼無法修改;
15.6 Spring aop中用到的技術
答:1.動態代理技術
2.asm字節碼框架
3.代理設計模式
15.7 事務的作用范圍
答:作用于service層,第一次訪問數據庫時開啟,service層執行完畢后,事務關閉/提交;
15.8 java中的不可變對象
答:String ,原生類型的包裝器如Integer、Double、Long,BigInteger,LocalDate等
2020.04.23 面試題新增
16.1 dubbo spi用到的設計模式
答:在加載類的時候用到了工廠的設計模式,在方法調用時用到了代理模式;通過Adaptive,在調用階段動態地根據參數決定調用哪個實現類。
16.2 dubbo的注冊原理
答:利用Javassist技術生成invoker,然后調用DubboProtocol的export方法,最終會調用ZookeeperRegistryFactory的createRegistry進行注冊;
16.3 redis分布式鎖與zk分布式鎖的對比
答:1.redis分布式鎖,其實需要自己不斷去嘗試獲取鎖,比較消耗性能;
2.zk分布式鎖,獲取不到鎖,注冊個監聽器即可,不需要不斷主動嘗試獲取鎖,性能開銷較小;
3.如果是redis獲取鎖的那個客戶端bug了或者掛了,那么只能等待超時時間之后才能釋放鎖,而zk的話,因為創建的是臨時znode,只要客戶端掛了,znode就沒了,此時就自動釋放鎖。
16.4 double check問題
答:寫單例類時,有時用到double check,這個時候還需要把單利對象用volatile修飾,這個有兩個作用
1.內存可見性;2.防止指令重排序;
因為new一個對象有四個步驟,1.堆內存分配;2.初始化值;3.在棧中創建引用變量;4.然后將堆內對象的地址賦值給引用變量;
如果不用volatile關鍵字,那么上面四部就會被打亂;
2020.11.24 新增
17.如何保證kafka有序消費
答:1.單線程,單分區,單消費者;
2.多線程生產,多quenue,多消費者就通過業務key去路由到指定的分區,使統一業務的key路由到同一個分區,同樣消費這也消費同一個分區;
18.線程池參數詳解,以及工作原理
答:①corePoolSize:線程池的核心線程數,說白了就是,即便是線程池里沒有任何任務,也會有corePoolSize個線程在候著等任務。
②maximumPoolSize:最大線程數,不管你提交多少任務,線程池里最多工作線程數就是maximumPoolSize。
③keepAliveTime:線程的存活時間。當線程池里的線程數大于corePoolSize時,如果等了keepAliveTime時長還沒有任務可執行,則線程退出。
⑤unit:這個用來指定keepAliveTime的單位,比如秒:TimeUnit.SECONDS。
⑥workQueue:一個阻塞隊列,提交的任務將會被放到這個隊列里。
⑦threadFactory:線程工廠,用來創建線程,主要是為了給線程起名字,默認工廠的線程名字:pool-1-thread-3。
⑧handler:拒絕策略,當線程池里線程被耗盡,且隊列也滿了的時候會調用。
1.ThreadPoolExecutor.AbortPolicy:丟棄任務并拋出RejectedExecutionException異常。
2.ThreadPoolExecutor.DiscardPolicy:丟棄任務,但是不拋出異常。如果線程隊列已滿,則后續提交的任務都會被丟棄,且是靜默丟棄。
3.ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然后重新提交被拒絕的任務。
4.ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務
19.jvm為什么1.8要將永久代變為元數據區?
答:1.字符串存在永久代中,容易出現性能問題和內存溢出;
2.類及方法的信息等比較難確定其大小,因此對于永久代的大小指定比,較困難,太小容易出現永久代溢出,太大則容易導致老年代溢出;
3.永久代會為 GC 帶來不必要的復雜度,并且回收效率偏低
4.將 HotSpot 與 JRockit 合二為一;
20.如何防止緩存擊穿
答:1.做無效key的空緩存;
2.利用bitmap實現過濾;
3.查庫之前先獲取分布式鎖;
21.當同一組kafka消費者數量大于kafka分區數的時候會出現什么?
答:有一部分消費者永遠都無法消費到數據;
22.seata支持高并發么?
答:不支持,因為整個業務期間需要獨占資源;
23.es的索引結構
答:倒排索引和正排索引;
24.mysql聚簇索引和非聚簇索引
答:1.聚簇索引葉子結點存放具體數據,且邏輯有序;非聚簇索引葉子節點存放聚簇索引的指針;
25.spring和spring boot的區別
答:spring boot是對spring做的擴展;
優點是:
1:創建獨立的spring應用。
2:嵌入Tomcat, Jetty Undertow 而且不需要部署他們。
3:提供的“starters” poms來簡化Maven配置
4:盡可能自動配置spring應用。
5:提供生產指標,健壯檢查和外部化配置
6:絕對沒有代碼生成和XML配置要求
26.eureka失效剔除的時間以及次數,客戶端的同步時間間隔等
答:1.心跳間隔時間:30s
2.收到最后一次心跳過多久剔除:默認90s
3.進入自我保護的默認時間:15分鐘內統計的心跳比例地低于85%時會進入
27.熔斷器的默認配置,什么時候產生熔斷
答:1.10s內出先20次失敗;
2.5s后重試;
3.錯誤率達到50%時也會短路;
28.mysql單表的數據量應控制在多少?
答:應該控制在500W左右,這與 MySQL 的配置以及機器的硬件有關;InnoDB buffer size 足夠的情況下,其能完成全加載進內存,查詢不會有問題。但是,當單表數據庫到達某個量級的上限時,導致內存無法存儲其索引,使得之后的 SQL 查詢會產生磁盤 IO,從而導致性能下降;
29.分表路由策略
1.對分表參數進行哈希并取余路由。
優點:請求可均衡分布。
缺點:當表庫變更,擴容時,會導致數據位置變更,處理繁瑣。因此需要提前規劃好容量,一次分好。
場景:在線高性能服務
2.將分表參數劃分為不同的范圍。不同范圍內的參數映射到不同的庫表。例如用時間,根據時間段劃分。
優點:當表庫變更,擴容時處理較方便。
缺點:可能導致請求不均勻,造成某個庫表被頻繁訪問,其它庫表訪問頻率較低。
場景:非在線高性能服務。日志服務。運營服務
30.聯合索引,最左匹配原則
答:當創建(col1,col2,col3)聯合索引時,相當于創建了(col)單列索引,(clo1,clo2)聯合索引以及(col1,col2,col3)聯合索引想要索引生效,只能使用col1和col1,col2和col1,col2,col3三種組合;當然,col1,col3組合也可以,但實際上只用到了col1的索引,col3并沒有用到!
31.線程池中的核心線程數是如何保活的?
答:死循環從quenue中獲取任務,當不為空時,返回任務,否則一直阻塞;
32.threadLocal原理
答:每個thread內部都維護了一個map,而這個map又是threadlocal維護的,由threadlocal對其進行get,set操作;這樣對于不同的線程,每次獲取副本值時,別的線程并不能獲取到當前線程的副本值,這樣就形成了副本隔離,互不干擾;
33.threadlocalmap是如何解決地址沖突的以及它擴容過程;
答:他是采用開放地址發解決地址沖突的,擴容時先清除key為null的value,然后在判斷當前容量是否大于負載因子的3/4,如果大于就進行擴容,擴容方式和hashmap差不多;
34.redis自己實現的數據結構有哪些?
答:
1.string -----> simple synamic string - 支持自動動態擴容的字節數組
2..list------>ziplist 壓縮列表,節省內存,存儲不同類型的數據
3.hash----->當保存的鍵值大小都小于64時且個數小于512時,使用ziplist,否則使用散列表
4.set------->當存儲數據都為整數且個數小于512個使用有序數組存儲,否則使用散列表
5.sortSet----->當所有數據的大小都要小于64字節且元素個數要小于128個使用壓縮表,否則使用跳表;