作者:GarbageCollection
來源:牛客網(wǎng)
基礎(chǔ)篇
一诊笤、面向?qū)ο笕筇匦?/p>
封裝、繼承、多態(tài)
二安接、集合
ArrayList和LinkedList區(qū)別:
ArrayList基于動態(tài)數(shù)組實現(xiàn)橙数。支持隨機訪問尽楔,但插入刪除的代價很高,需要移動大量元素
LinkedList基于雙向鏈表實現(xiàn)拱她。不支持隨機訪問,但插入刪除方便扔罪,只需要改變指針
HashMap實現(xiàn)原理:
HashMap的內(nèi)部存儲結(jié)構(gòu)其實是數(shù)組+鏈表+樹秉沼。當實例化一個HashMap時,會初始化默認長度(initialCapacity)和加載因子(loadFactor)矿酵,系統(tǒng)會創(chuàng)建一個長度為默認長度(initialCapacity)的Node數(shù)組唬复,這個長度在哈希表中被稱為容量(Capacity),在這個數(shù)組中可以存放元素的位置稱之為“桶”(bucket)全肮,每個桶(bucket)都有自己的索引敞咧,系統(tǒng)可以根據(jù)索引快速的查找桶(bucket)中的元素。
每個桶(bucket)中存儲一個元素辜腺,即一個Node對象休建。每一個Node對象可以帶一個引用變量next,用于指向下一個元素评疗。因此在一個桶(bucket)中测砂,就有可能生成一個Node鏈,也可能是一個個TreeNode對象百匆,每個TreeNode對象可以有兩個葉子節(jié)點left和right邑彪。因此在一個桶(bucket)中就有可能生成一個TreeNode樹。新添加的元素作為鏈表的last或樹的葉子節(jié)點胧华。
HashMap擴容:當HashMap中的元素個數(shù)超過數(shù)組大小(initialCapacity)加載因子(loadFactor)時寄症,就會進行數(shù)組擴容宙彪,加載因子(loadFactor)的默認值是0.75。默認情況下有巧,數(shù)組大小(initialCapacity)為16释漆,那么當HashMap中元素個數(shù)超過160.75=12(此值為代碼中的threshold值,即臨界值)時篮迎,就把數(shù)組的大小擴展為2*16=32男图,即擴大一倍,然后重新計算每個元素在數(shù)組中的位置甜橱,這是一個非常消耗性能的操作逊笆,所以如果我們已經(jīng)能預知HashMap中元素的個數(shù),那么預設(shè)元素的個數(shù)能夠有效的提高其性能岂傲。
HashMap樹形化:當HashMap中其中一個鏈的對象個數(shù)如果達到了8個难裆,此時如果capacity沒有達到64,那么HashMap會先擴容解決镊掖。若達到64乃戈,那么這個鏈會轉(zhuǎn)化為樹,節(jié)點類型由Node變?yōu)門reeNode亩进。如果對象被刪除后症虑,下次resize方法時判斷樹的節(jié)點個數(shù)低于六個,則會把樹轉(zhuǎn)化為鏈表归薛。
JDK1.8相較于之前的變化
HashMap map = new HashMap();//默認情況下谍憔,先不創(chuàng)建長度為16的數(shù)組
當首次調(diào)用map.put()時,再創(chuàng)建長度為16的數(shù)組
數(shù)組為Node類型主籍,在jdk7中稱為Entry類型
形成鏈表結(jié)構(gòu)時韵卤,新添加的key-value對在鏈表的尾部(七上八下)
當數(shù)組指定索引位置的鏈表長度>8時,且map中的數(shù)組的長度> 64時崇猫,此索引位置上的所有key-value對使用紅黑樹進行存儲沈条。
負載因子值的大小,對HashMap有什么影響诅炉?
負載因子的大小決定了HashMap的數(shù)據(jù)密度蜡歹。
負載因子越大密度越大,發(fā)生碰撞的幾率越高涕烧,數(shù)組中的鏈表越容易長,成查詢或插入時的比較次數(shù)增多月而,性能會下降。
負載因子越小议纯,就越容易觸發(fā)擴容父款,數(shù)據(jù)密度也越小,意味著發(fā)生碰撞的幾率越小,數(shù)組中的鏈表也就越短憨攒,查詢和插入時比較的次數(shù)也越小世杀,性能會更高。但是會浪費一定的內(nèi)容空間肝集。而且經(jīng)常擴容也會影響性能瞻坝,建議初始化預設(shè)大一點的空間。
按照其他語言的參考及研究經(jīng)驗杏瞻,會考慮將負載因子設(shè)置為0.7~0.75所刀,此時平均檢索長度接近于常數(shù)。
談?wù)勀銓ashMap中put/get方法的認識捞挥?如果了解再談?wù)凥ashMap的擴容機制浮创?默認大小是多少?什么是負載因子(或填充比)砌函?什么是吞吐臨界值(或閾值斩披、threshold)?
put():
當put(k胸嘴,v)時雏掠,創(chuàng)建一個長度為16的Node數(shù)組
首先按照k所在類的hashcode()計算其哈希值斩祭,用哈希值計算出k位于哪個桶
如果該桶為空劣像,則直接插入
如果該桶不為空
用鏈式方法插入摧玫。如果鏈的長度達到臨界值耳奕,則把鏈表轉(zhuǎn)化為紅黑樹
如果桶中存在重復的k,則為該k替換新值
如果size大于閾值诬像,則進行擴容
get():
通過哈希值找到該k映射的桶
桶上的k就是要查找的k屋群,直接命中
桶上的 key 不是要查找的 key,則查看后續(xù)節(jié)點:
如果后續(xù)節(jié)點是樹節(jié)點坏挠,通過調(diào)用樹的方法查找該 key
如果后續(xù)節(jié)點是鏈式節(jié)點芍躏,則通過循環(huán)遍歷鏈查找該 key
擴容機制:默認容量為16,負載因子為0.75降狠,如果使用容量超過16×0.75=12(閾值)時对竣,進行擴容,擴容至一倍(2×16=32)榜配,并重新計算每個元素的位置
創(chuàng)建HashSet會創(chuàng)建一個初始值為16否纬,負載因子為0.75的HashMap,key為add()的值蛋褥,value為Object類型常量
HashMap和HashTable區(qū)別
HashTable中的方法是synchronized的
HashMap可以插入鍵為null临燃,HashTable不可以
HashMap不能保證對著時間的推移Map中的元素次序是不變的
HashTable直接使用對象的hashCode,HashMap要重新計算
AVL和紅黑樹的區(qū)別
AVL是嚴格的平衡樹,在增加或刪除節(jié)點的時候膜廊,旋轉(zhuǎn)的次數(shù)比紅黑樹要多乏沸。紅黑樹用非嚴格的平衡來減少旋轉(zhuǎn)次數(shù)
紅黑樹根節(jié)點是黑色,葉子節(jié)點都為黑色且空溃论,每一個紅色節(jié)點的兩個子節(jié)點都是黑色
AVL靠平衡因子旋轉(zhuǎn)年扩,紅黑樹靠節(jié)點顏色以及一些約定旋轉(zhuǎn)
AVL更適合搜索寇损,紅黑樹更適合增刪
并發(fā)篇
一、java如何開啟線程?如何保證線程安全平挑?
線程和進程的區(qū)別:進程是操作系統(tǒng)進行資源分配的最小單元,線程是操作系統(tǒng)進行任務(wù)分配的最小單元癞志,線程屬于進程
進程擁有資源而線程不擁有锣笨,線程可以訪問隸屬進程的資源
線程是獨立調(diào)度的基本單位,同一進程中線程切換不會引起進程切換菲驴,不通進程中的線程切換會引起進程切換
創(chuàng)建切換進程會產(chǎn)生極大的系統(tǒng)開銷(內(nèi)存荐吵、I/O、CPU)赊瞬,而線程不會(只需設(shè)置和保存少量寄存器)
線程可以通過同一進程中的數(shù)據(jù)進行通信先煎,進程間通信需要借助IPC
進程間通信方式:
管道通信: 一種半雙工的通信方式,數(shù)據(jù)只能單向流動巧涧,且只能在具有親緣關(guān)系的進程間使用薯蝎。進程的親緣關(guān)系通常指父子進程關(guān)系
命名管道通信:與管道通信相似,但是它允許無親緣關(guān)系進程間的通信
信號量: 是一個計數(shù)器谤绳,可以用來控制多個進程對共享資源的訪問占锯。它常作為一種鎖機制,防止多個進程訪問同一資源缩筛。主要作為進程間以及不同線程之間的同步手段
消息隊列:由消息產(chǎn)生的鏈表消略,存放在內(nèi)核中并由消息隊列標識符標識。其克服了信號量傳遞少瞎抛、管道只能承載無格式字節(jié)流以及緩沖區(qū)受限等缺點
共享內(nèi)存:就是映射一段能被其他進程所訪問的內(nèi)存艺演,這段共享內(nèi)存由一個進程創(chuàng)建,但多個進程可以訪問桐臊。共享內(nèi)存是最快的IPC方式胎撤,它使很對其他進程間通信方式運行效率低而專門設(shè)計的。它往往與其他通信機制來配合使用來實現(xiàn)進程間的同步和通信
套接字:可以用于本地進程間通信和不同主機進程間通信
線程通信方式:
共享內(nèi)存:線程之間通過讀寫內(nèi)存中的公共狀態(tài)來隱式通信
消息傳遞:線程之間沒有公共狀態(tài)豪硅,通過明確的發(fā)送信息來顯示的進行通信
管道流
開啟線程4種方式:
繼承Thread類哩照,重寫run方法
實現(xiàn)Runnable接口,實現(xiàn)run方法
實現(xiàn)Callable接口懒浮,實現(xiàn)call方法
創(chuàng)建線程池
保證線程安全:加鎖
JVM提供的鎖:Synchronized關(guān)鍵字
JDK提供的鎖:Lock
線程的狀態(tài)
新建:創(chuàng)建一個線程但還沒有調(diào)用start方法
就緒:調(diào)用start方法飘弧,進入隊列等待cpu時間片识藤,具備運行條件,沒有被分配到cpu資源
運行:就緒的線程獲取到cpu資源
阻塞:遇到鎖或人為掛起時次伶,讓cpu臨時中止自己的執(zhí)行
死亡:線程完成了它的任務(wù)或被提前強制性中止或出現(xiàn)異常導致結(jié)束
二痴昧、Volatile和Synchronized有什么區(qū)別? Volatile能不能保證線程安全?DCL(Double Check Lock)單例為什么要加Volatile?
Synchronized用來加鎖,Volatile保證變量的內(nèi)存可見性
不能冠王。Volatile不能保證原子性
Volatile可以防止指令重排
三赶撰、JAVA線程鎖機制是怎樣的?偏向鎖、輕量級鎖柱彻、重量級鎖有什么區(qū)別?鎖機制是如何升級的?
JAVA的鎖就是在對象頭中記錄一個鎖狀態(tài)
JAVA的鎖機制就是根據(jù)資源競爭的激烈程度不斷進行鎖升級的過程
synchronized 鎖升級原理:在鎖對象的對象頭里面有一個 threadid 字段豪娜,在第一次訪問 的時候 threadid 為空,jvm 讓其持有偏向鎖哟楷,并將 threadid 設(shè)置為其線程 id瘤载,再次進 入的時候會先判斷 threadid 是否與其線程 id 一致,如果一致則可以直接使用此對象卖擅, 如果不一致鸣奔,則升級偏向鎖為輕量級鎖,通過自旋循環(huán)一定次數(shù)來獲取鎖惩阶,執(zhí)行一定次數(shù)之 后挎狸,如果還沒有正常獲取到要使用的對象,此時就會把鎖從輕量級升級為重量級鎖断楷,此過程 就構(gòu)成了 synchronized 鎖的升級锨匆。 鎖的升級的目的:鎖升級是為了減低了鎖帶來的性能消耗。在 Java 6 之后優(yōu)化 synchronized 的實現(xiàn)方式脐嫂,使用了偏向鎖升級為輕量級鎖再升級到重量級鎖的方式统刮,從而減低了鎖帶來的性能消耗
四紊遵、談?wù)勀銓QS的理解账千。AQS如何實現(xiàn)可重入鎖?
AQS(抽象隊列同步器)是一個JAVA線程同步框架,是JDK中很多鎖工具的核心實現(xiàn)框架暗膜。在AQS中匀奏,維護了一個信號量state和一個線程組成的雙向鏈表。其線程隊列就是用來給線程排隊的学搜,而state就像是一個紅綠燈來控制線程等待或放行娃善。在不同的場景下有不同的意義。
在可重入鎖下瑞佩,state用來標識加鎖的次數(shù)聚磺。0表示無鎖,每加一次鎖state加1炬丸,每釋放一次state減1瘫寝。
五蜒蕾、CAS是什么?CAS底層原理焕阿?為什么不用Synchronized而用CAS咪啡?CAS缺點?ABA問題暮屡?
CAS(CompareAndSwap撤摸,比較并交換):是一條CPU并發(fā)原語,功能是判斷內(nèi)存某個位置的值是否為預期值褒纲,如果是則更改為新的值准夷,這個過程是原子的
底層原理:JAVA的CAS操作都依賴UnSafe類中的方法,UnSafe時CAS的核心類莺掠,其中含有大量的native方法冕象,可以直接調(diào)用操作系統(tǒng)底層資源操作特定內(nèi)存中的數(shù)據(jù)
比較:Synchronized只允許有一個線程訪問,保證一致性但沒有提高并發(fā)性汁蝶,而CAS既保證了一致性渐扮,又提高了并發(fā)性
CAS缺點:
循環(huán)時間長,CPU開銷大
只能保證一個共享變量的原子操作
ABA問題:因為 CAS算法是在某一時刻取出內(nèi)存值然后在當前的時刻進行比較掖棉,中間存在一個時間差墓律,在這個時間差里就可能會產(chǎn)生 ABA 問題。
當有兩個線程 T1 和 T2 從內(nèi)存中獲取到值A(chǔ)幔亥,線程 T2 通過某些操作把內(nèi)存 值修改為B耻讽,然后又經(jīng)過某些操作將值修改為A,T2退出帕棉。線程 T1 進行操作的時候 针肥,使用預期值同內(nèi)存中的值比較,此時均為A香伴,修改成功退出慰枕。但是此時的A以及不是原先的A了
解決ABA問題:
AtomicReference類,原子引用
AtomicStampedReference即纲,時間戳原子引用
六具帮、集合類不安全,ConcurrentHashMap原理低斋?
故障現(xiàn)象:java.util.ConcurrentModificationException
解決方案:以ArrayList為例
new Vector
Collections.synchronizedList()
CopyOnWriteArrayList()蜂厅,寫時拷貝,(map是ConcurrentHashMap(其原理是使用了分段鎖))
七膊畴、各種鎖
公平鎖與非公平鎖:ReentrantLock(默認非公平鎖)
公平鎖:按申請鎖的順序來獲取鎖掘猿。排隊,先來后到
非公平鎖:后申請鎖的線程有可能比先申請的線程優(yōu)先獲取鎖唇跨。高并發(fā)下可能出現(xiàn)優(yōu)先級反轉(zhuǎn)或饑餓現(xiàn)象
可重入鎖(遞歸鎖):A同步方法中調(diào)用同步方法B稠通,B也可以獲得A的鎖礁遵,A和B擁有同一把鎖
Synchronized和ReentrantLock都是可重入鎖
自旋鎖(SpinLock):嘗試獲取鎖的線程不會立即阻塞,而是采用循環(huán)的方式去嘗試獲取鎖
獨占(寫)鎖:該鎖只能被一個線程所持有
共享(讀)鎖:該鎖可被多個線程所持有
八采记、CountDownLatch佣耐、CyclicBarrier、Semaphore唧龄、LockSupport
CountDownLatch:一個同步工具類兼砖,用來協(xié)調(diào)多個線程之間的同步,或者說起到線程之間的通信既棺。它能夠使一個線程在等待另外一些線程完成各自工作之后讽挟,再繼續(xù)執(zhí)行。使用一個計數(shù)器進行實現(xiàn)丸冕。計數(shù)器初始值為線程的數(shù)量耽梅。當每一個線程完成自己任務(wù)后,計數(shù)器的值就會減一胖烛。當計數(shù)器的值為0時眼姐,表示所有的線程都已經(jīng)完成一些任務(wù),然后在CountDownLatch上等待的線程就可以恢復執(zhí)行接下來的任務(wù)
CyclicBarrier:與CountDownLatch相反佩番。初始值為0众旗,達到要求后才可以恢復執(zhí)行接下來的任務(wù)
Semaphore:
用于多個共享資源的互斥(搶車位)
用于并發(fā)線程數(shù)的控制
LockSupport:用來創(chuàng)建鎖和其他同步類的基本線程阻塞原語,它使用了一種許可證的概念來做到阻塞和喚醒線程的功能趟畏,許可證最多有一個贡歧。其park()與wait()類似,unpark()與notify()類似
九赋秀、阻塞隊列利朵?
當隊列為空時,從隊列中獲取元素的操作將會被阻塞猎莲;當隊列為滿時绍弟,往隊列里添加元素的操作將會被阻塞
優(yōu)點:不需要關(guān)心什么時候需要阻塞線程,什么時候需要喚醒線程
BlockingQueue接口實現(xiàn)Collection接口益眉,與List平級
ArrayBlockingQueue:由數(shù)組構(gòu)成的有界阻塞隊列
LinkedBlockingQueue:由鏈表構(gòu)成的有界(但大小默認為Integer.MaxValue)阻塞隊列
synchronousQueue:只存儲單個元素的隊列
十晌柬、Synchronized和Lock有什么區(qū)別姥份?
原始構(gòu)成
synchronized是關(guān)鍵字郭脂,屬于JVM層面
Lock是具體類,屬于API層面
使用方法
synchronized不需要手動釋放澈歉,當其執(zhí)行完后系統(tǒng)會讓線程釋放對鎖的占用
Lock需要用戶去手動釋放鎖展鸡。若沒有釋放,可能會出現(xiàn)死鎖
等待是否可中斷
synchronized除非拋異嘲D眩或者運行完成莹弊,否則不可中斷
Lock可中斷涤久。可設(shè)置超時方法
加鎖是否公平
synchronized非公平鎖
Lock兩種都可
鎖綁定多個條件Condition
synchronized沒有
Lock可精確喚醒忍弛,而不是像synchronized要么喚醒一個响迂,要么喚醒全部
十一、線程池
線程池優(yōu)勢:降低資源消耗细疚,提高響應(yīng)速度蔗彤,提高線程的可管理性
線程池7大參數(shù)
核心線程數(shù)
最大線程數(shù):必須大于1
存活時間:當前線程數(shù)大于核心線程數(shù)、空閑時間達到存活時間時疯兼,多余的線程會被銷毀
時間單位
阻塞隊列:被提交但未被執(zhí)行的任務(wù)
創(chuàng)建工廠:創(chuàng)建工作線程的工廠然遏。一般默認
拒絕策略:當隊列滿了并且工作線程等于最大線程數(shù)時如何拒絕后來的任務(wù)
AbortPolicy(默認):拋異常阻止運行
CallRunSPolicy:將某些任務(wù)回退到調(diào)用者執(zhí)行
DiscardOldestPolicy:拋棄隊列中等待最久的任務(wù),將當前任務(wù)加入到隊列中
DiscardPolicy:直接丟棄任務(wù)(允許任務(wù)丟失的最好方案)
線程池的底層工作原理
小于核心線程數(shù)吧彪,立即運行此任務(wù)
大于等于核心線程數(shù)待侵,放入隊列等待
隊列滿且小于最大線程數(shù),創(chuàng)建非核心線程立即運行此任務(wù)
等于最大線程數(shù)姨裸,啟用拒絕策略
空閑線程超過存活時間時秧倾,判斷大于核心線程數(shù),將其銷毀
線程池完成所有任務(wù)后會收縮到核心線程池的大小
線程池合理配置參數(shù)
CPU密集型:CPU核數(shù) + 1
IO密集型
CPU核數(shù) * 2
CPU核數(shù)/(1 - 阻塞系數(shù)) 阻塞系數(shù)0.8-0.9
死鎖檢查:jps -l -> jstack 進程號
網(wǎng)絡(luò)篇
一傀缩、TCP和UDP有什么區(qū)別?TCP為什么是三次握手中狂,而不是兩次?
TCP(Transfer ControlProtocol)是一種面向連接的、可靠的傳輸層通信協(xié)議
特點:面向連接扑毡;點對點胃榕;高可靠的;效率較低瞄摊;占用系統(tǒng)資源較多
UDP(User DatagramProtocol)是一種無連接的勋又、不可靠的傳輸層通信協(xié)議
特點:不需要鏈接;可進行廣播發(fā)送换帜;傳輸不可靠楔壤,有可能丟失消息;效率較高惯驼;占用系統(tǒng)資源較少
TCP三次握手過程:
服務(wù)端一直處于監(jiān)聽狀態(tài)蹲嚣,等待客戶端的連接請求
第握手一次:客戶端向服務(wù)端發(fā)送連接請求報文。SYN=1祟牲,ACK=0隙畜,初始序號x
第二次握手:服務(wù)端收到請求報文,如果同意建立連接说贝,則向客戶端發(fā)送確認報文议惰。SYN=1,ACK=1乡恕,確認號x+1言询,初始序號y(客戶端發(fā)送功能正常俯萎,服務(wù)端接收功能正常)
第三次握手:客戶端收到服務(wù)端的確認報文,也要想服務(wù)端進行確認运杭。ACK=1夫啊,x+1,y+1(服務(wù)端發(fā)送功能正常辆憔,客戶端接收功能正常)
TCP四次揮手過程:
第一次揮手:客戶端發(fā)送釋放連接報文涮母。FIN=1,初始序號u
第二次揮手:服務(wù)端收到釋放報文并向客戶端發(fā)送確認報文(此時TCP處于半關(guān)閉狀態(tài))躁愿。ACK=1叛本,u+1,初始序號v
服務(wù)端進入CLOSE-WAIT狀態(tài)彤钟,服務(wù)端將未傳輸完的數(shù)據(jù)繼續(xù)傳輸
第三次揮手:服務(wù)端傳輸完數(shù)據(jù)来候,向客戶端發(fā)出釋放連接報文。FIN=1逸雹,ACK=1营搅,u+1,初始序號w
第四次揮手:客戶端收到釋放報文并向服務(wù)端發(fā)送確認報文梆砸。u+1转质,w+1
客戶端進入TIME-WAIT狀態(tài),等待2MSL時間再進入CLOSE狀態(tài)帖世。
確認發(fā)送的最后一段報文能到達服務(wù)端休蟹。如果服務(wù)端沒收到客戶端的確認報文,服務(wù)端就會重新向客戶端發(fā)送釋放連接報文
讓本連接持續(xù)時間內(nèi)所產(chǎn)生的報文都從網(wǎng)絡(luò)中消失日矫,使下一個新的連接不會出現(xiàn)舊連接報文
二赂弓、JAVA有哪幾種IO模型?有什么區(qū)別?
BIO 同步阻塞IO:用戶線程發(fā)起IO讀/寫操作之后,線程阻塞哪轿,直到可以開始處理數(shù)據(jù)
可靠性差盈魁,吞吐量低,適用于較少且比較固定的場景
1
2
3
4
5
Client------> Thread <-----
???????????????????????????|
Client------> Thread <-----------Server
???????????????????????????|
Client------> Thread <-----
NIO 同步非阻塞IO:發(fā)起IO請求之后可以立即返回窃诉,如果沒有就緒的數(shù)據(jù)杨耙,需要不斷地發(fā)起IO請求直到數(shù)據(jù)就緒
可靠性較好,吞吐量較高飘痛,適用于連接較多且連接較短(輕操作)珊膜,編程模型最復雜
1
2
3
4
5
Client----
??????????↓
Client----> selector---> Thread <----Server
??????????↑
Client----
AIO 異步非阻塞IO:用戶線程發(fā)出IO請求之后,繼續(xù)執(zhí)行敦冬,由內(nèi)核進行數(shù)據(jù)的讀取并放在用戶指定的緩沖區(qū)內(nèi)辅搬,在IO完成之后通知用戶線程直接使用。
可靠性最好脖旱,吞吐量最高堪遂,適用于連接較多且連接較長(重操作),編程模型較簡單萌庆,需要操作系統(tǒng)支持
1
2
3
4
5
6
|→Client----
|?????????? ↓
|→Client----> selector---> Thread <----Server
|?????????? ↑??????????????????????????? |
|→Client----???????????????????????????? |
|_______________________________________↙
三溶褪、JAVA NIO的幾個核心組件是什么?分別有什么作用?
Buffer(緩沖區(qū)):緩沖區(qū)的出現(xiàn)導致了NIO和BIO 的不同。讀數(shù)據(jù)時可以先讀一部分到緩沖區(qū)中践险,然后處理其他事情猿妈;寫數(shù)據(jù)時可以先寫一部分到緩沖區(qū)中,然后處理其他事情巍虫。讀和寫可以不再持續(xù)彭则,所以不會阻塞。當緩沖區(qū)滿后才會將其真正的進入讀寫
Channel:NIO的所有操作都從Channle開始占遥。
從通道進行數(shù)據(jù)讀雀┒丁:創(chuàng)建一個緩沖區(qū),然后請求通道讀取數(shù)據(jù)
從通道進行數(shù)據(jù)寫入:創(chuàng)建一個緩沖區(qū)瓦胎,填充數(shù)據(jù)芬萍,并要求通道寫入數(shù)據(jù)
Selector:選擇器可以讓單個線程處理多個通道,達到復用的目的
四搔啊、描述下HTTP和HTTPS的區(qū)別
HTTP:是互聯(lián)網(wǎng)上應(yīng)用最廣泛的以中網(wǎng)絡(luò)通信協(xié)議柬祠,基于TCP協(xié)議,可以使瀏覽器工作更高效负芋,減少網(wǎng)絡(luò)傳輸
狀態(tài)碼
1xx(信息性狀態(tài)碼):接受的請求正在處理
2xx(成功狀態(tài)碼):請求正常處理完畢
3xx(重定向狀態(tài)碼):需要進行附加的操作來完成請求
4xx(客戶端錯誤狀態(tài)碼):服務(wù)器無法處理請求
5xx(服務(wù)器錯誤狀態(tài)碼):服務(wù)器內(nèi)部錯誤
HTTPS:是HTTP的加強版漫蛔,可以認為是HTTP+SSL(Secure Socket Layer)。在HTTP的基礎(chǔ)上增加了一系列的安全機制旧蛾。一方面保證數(shù)據(jù)傳輸安全惩猫,另一方面對訪問者增加了驗證機制。是目前現(xiàn)行架構(gòu)下蚜点,最為安全的解決方案
區(qū)別:
HTTP的連接是簡單無狀態(tài)的轧房;HTTPS的數(shù)據(jù)傳輸時經(jīng)過證書加密的,安全性更高
HTTP是免費的绍绘;HTTPS需要申請證書奶镶,證書是要收費的
它們的傳輸協(xié)議不同,所以端口也不同陪拘,HTTP默認:80 HTTPS默認:443
HTTPS的缺點:
HTTPS的握手協(xié)議比較費時厂镇,所以會影響服務(wù)的響應(yīng)速度以及吞吐量
功能越強大的證書費用越高
五、計算機網(wǎng)絡(luò)體系結(jié)構(gòu)
應(yīng)用層:為特定應(yīng)用程序提供數(shù)據(jù)傳輸服務(wù)左刽,如HTTP捺信、DNS等協(xié)議。數(shù)據(jù)單位為報文。
表示層:負責數(shù)據(jù)格式的轉(zhuǎn)換迄靠。將應(yīng)用處理的信息轉(zhuǎn)換為適合網(wǎng)絡(luò)傳輸?shù)母袷健?/p>
會話層:自動發(fā)包秒咨,自動尋址。建立和管理應(yīng)用程序之間的通訊
傳輸層:為進程提供通用數(shù)據(jù)傳輸服務(wù)掌挚。由于應(yīng)用層協(xié)議很多雨席,定義通用的傳輸層協(xié)議就可以支持不斷增多的應(yīng)用層協(xié)議。運輸層包括兩種協(xié)議:傳輸控制協(xié)議 TCP吠式,提供面向連接陡厘、可靠的數(shù)據(jù)傳輸服務(wù),數(shù)據(jù)單位為報文段特占;用戶數(shù)據(jù)報協(xié)議 UDP糙置,提供無連接、盡最大努力的數(shù)據(jù)傳輸服務(wù)是目,數(shù)據(jù)單位為用戶數(shù)據(jù)報谤饭。TCP 主要提供完整***,UDP 主要提供及時***胖笛。
網(wǎng)絡(luò)層:為主機提供數(shù)據(jù)傳輸服務(wù)网持。而傳輸層協(xié)議是為主機中的進程提供數(shù)據(jù)傳輸服務(wù)。網(wǎng)絡(luò)層把傳輸層傳遞下來的報文段或者用戶數(shù)據(jù)報封裝成分組长踊。
數(shù)據(jù)鏈路層:網(wǎng)絡(luò)層針對的還是主機之間的數(shù)據(jù)傳輸服務(wù)功舀,而主機之間可以有很多鏈路,鏈路層協(xié)議就是為同一鏈路的主機提供數(shù)據(jù)傳輸服務(wù)身弊。數(shù)據(jù)鏈路層把網(wǎng)絡(luò)層傳下來的分組封裝成幀辟汰。
物理層:考慮的是怎樣在傳輸媒體上傳數(shù)據(jù)比特流,而不是指具體的傳輸媒體阱佛。作用是盡可能屏蔽傳輸媒體和通信手段的差異帖汞,使數(shù)據(jù)鏈路層感覺不到這些差異。
TCP/IP只有四層凑术,相當與五層協(xié)議中數(shù)據(jù)鏈路層和物理層合并為網(wǎng)絡(luò)接口層翩蘸。它不嚴格遵循OSI分層概念,應(yīng)用層可能會直接使用IP層或者網(wǎng)絡(luò)接口層
六淮逊、訪問一個url涉及到了那些東西催首?
到DNS服務(wù)器將url解析為IP地址
得到IP地址后使用TCP協(xié)議進行連接
連接完成后發(fā)起HTTP請求
服務(wù)器響應(yīng)HTTP請求
瀏覽器解析代碼并請求資源
斷開TCP連接
瀏覽器渲染頁面
JVM篇
一、JVM的內(nèi)存模型
程序計數(shù)器
本地方法棧
虛擬機棧:棧幀(最小單位):局部變量表泄鹏、操作數(shù)棧郎任、方法返回地址、動態(tài)鏈接备籽、一些附加信息
堆:新生代(Eden區(qū)舶治、s0、s1)、老年代
方法區(qū):永久代←1.8→元空間
二霉猛、JAVA類加載的全過程是怎樣的涉波?什么是雙親委派機制泽论?有什么作用久妆?一個對象從加載到JVM窒升,再到被GC清除铸磅,都經(jīng)歷了什么過程赡矢?
全過程
加載階段(引導類加載器(BootStrap ClassLoader)→擴展類加載器(Extension ClassLoader)→系統(tǒng)類加載器(Application ClassLoader))
通過一個類的全限定名獲取定義此類的二進制字節(jié)流
將該字節(jié)流所代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運行時數(shù)據(jù)結(jié)構(gòu)
在內(nèi)存中生成一個代表該類的Class對象,作為方法區(qū)中該類各種數(shù)據(jù)的訪問入口
鏈接階段
驗證:確保Class文件的字節(jié)流中包含信息符合當前虛擬機要求阅仔,保證被加載類的正確性吹散,不會危害虛擬機自身安全
準備:為類變量(不包含用final修飾的static變量)分配內(nèi)存并且設(shè)置該類變量的默認值
解析:將常量池的符號引用替換為直接引用的過程。往往在初始化階段之后再開始
初始化階段:執(zhí)行類構(gòu)造器方法()的過程
雙親委派機制
如果一個類加載器收到了類加載請求八酒,它并不會自己先去加載空民,而是把這個請求委托給父類的加載器去執(zhí)行
如果父類加載器還存在其父類加載器,則進一步向上委托羞迷,請求最終將到達頂層的引導類加載器
如果父類加載器可以完成類加載任務(wù)界轩,就成功返回,若無法完成此加載任務(wù)衔瓮,子加載器才會嘗試自己去加載
作用:
避免類的重復加載
保護程序安全浊猾,防止核心API被隨意篡改
如何打破雙親委派機制:自定義一個類加載器,繼承ClassLoader類热鞍;重寫findClass()和loadClass()
過程:
JVM首先需要到方法區(qū)找到對象的類型信息葫慎,創(chuàng)建對象
JVM要實例化一個對象,會在堆中創(chuàng)建一個對象
對象首先分配到Eden區(qū)薇宠,經(jīng)過一次Minor GC偷办,對象存活就會進入S區(qū)。如果對象經(jīng)歷GC一直存活澄港,就會在S區(qū)來回拷貝椒涯,每移動一次,年齡就會加一回梧。當年齡到15時废岂,就會 將對象轉(zhuǎn)入老年代。
當方法執(zhí)行結(jié)束后漂辐,棧中的指針會被移除掉
對象經(jīng)過Full GC后會被垃圾回收器回收
三泪喊、怎么確定一個對象到底是不是垃圾?什么是GC Root髓涯?
引用計數(shù)法:每個對象都有一個計數(shù)器袒啼,如果計數(shù)器為0,則這個對象是垃圾。它無法解決循環(huán)引用的問題(內(nèi)存泄漏)
可達性分析法:以GC Roots為起始點蚓再,按照從上至下的方式搜索被根對象集合所連接的目標對象是否可達滑肉,如果不可達,則被認為是垃圾
GC Root:
虛擬機棧中引用的對象
本地方法棧內(nèi)引用的對象
方法區(qū)中類靜態(tài)屬性引用的對象
方法區(qū)中常量引用的對象
所有被同步鎖synchronized持有的對象
虛擬機內(nèi)部的引用
四摘仅、JVM有哪些垃圾回收算法
標記-清除:使用可達性分析算法將可達的對象標記起來靶庙,然后回收不可達的對象(會產(chǎn)生大量的內(nèi)存碎片)
復制:將內(nèi)存分為兩份,每次只使用一份娃属,回收時將可達的對象依次復制到另一份中六荒,回收當前份內(nèi)存(空間浪費,效率與存活對象的個數(shù)有關(guān))
標記-壓縮:先完成一次標記清楚算法后矾端,再進行一次內(nèi)存碎片整理
五掏击、JVM有哪些垃圾回收器?他們都是怎么工作的?什么是STW?他都發(fā)生在哪些階段?什么是三色標記?為什么要設(shè)計這么多的垃圾回收器?
STW:Stop The World。在垃圾回收執(zhí)行過程中秩铆,需要暫停用戶線程使垃圾回收線程運行
垃圾回收器
Serial(復制) - Serial Old(標整)串行
ParNew(復制) - CMS(標清)
Parallel(復制) - Parallel Old(標整)JDK1.8默認
G1(整體標整砚亭,局部復制)JDK1.9默認
ZGC
CMS垃圾回收器工作原理:耗時最長為②④步,總體來看為并發(fā)執(zhí)行
初始標記:標記GCRoots能直接關(guān)聯(lián)的對象殴玛,觸發(fā)STW
并發(fā)標記:進行GCRoots的跟蹤過程捅膘,和用戶線程一起工作,主要標記過程
重新標記:修正在并發(fā)標記期間滚粟,因用戶程序繼續(xù)運行而導致標記產(chǎn)生變動的那一部分對象的標記記錄寻仗,觸發(fā)STW
并發(fā)清除:清除垃圾
三色標記:CMS核心算法,將每個對象分成三種顏色
黑色:表示自己和成員變量都已經(jīng)標記完成
灰色:自己標記完成坦刀,成員變量標記未完成
白色:自己未標記完成
由于內(nèi)存越來越大愧沟,所以需要更強大的垃圾回收器來管理內(nèi)存
六、如何進行JVM調(diào)優(yōu)?JVM參數(shù)有哪些?怎么查看一個JAVA進程的JVM參數(shù)?談?wù)勀懔私獾腏VM參數(shù)鲤遥。
JVM調(diào)優(yōu)主要是通過定制JVM運行參數(shù)來提高應(yīng)用程序的運行速度
JVM參數(shù):
-開頭:標準指令沐寺,HotSpot支持的參數(shù),java -help打印
-X開頭:非標準指令盖奈,與特定HotSpot版本對應(yīng)混坞,java -X打印
-Xms512m:設(shè)置堆初始值
-Xmn512m:新生代內(nèi)存配置
-Xss256k:每個線程棧最大值
-XX開頭:不穩(wěn)定參數(shù),與特定HotSpot版本對應(yīng)且變化非常大
-XX+PrintCommandLineFlags:查看當前命令的不穩(wěn)定指令
-XX+PrintFlagsInitial:查看所有不穩(wěn)定指令的默認值
-XX+PrintFlagsFinal:查看所有不穩(wěn)定指令最終生效的實際值
-XX:+UseG1GC:開啟G1垃圾收集器
-XX:-UseG1GC:關(guān)閉G1垃圾收集器
七钢坦、OOM相關(guān)
SOFE StackOverFlowError:棧溢出究孕。遞歸。是錯誤
OOM java heap space:堆內(nèi)存不足
OOM GC overhead limit exceeded:大量資源在進行垃圾回收且回收很少
OOM Direct buffer memory:NIO寫入本地內(nèi)存而不是堆內(nèi)存爹凹,本地內(nèi)存可能用光
OOM unable to create new native thread:一個進程創(chuàng)建多個線程厨诸,超過系統(tǒng)承載極限。服務(wù)器不允許應(yīng)用創(chuàng)建這么多線程
OOM Metaspace:元空間內(nèi)存不足
MQ篇
一禾酱、MQ有什么用微酬?有哪些具體的使用場景
MQ:消息隊列绘趋。隊列是一種FIFO(先進先出)的數(shù)據(jù)結(jié)構(gòu)。消息由生產(chǎn)者發(fā)送到MQ進行排隊颗管,然后由消費者對消息進行處理
作用:
異步:提高系統(tǒng)的響應(yīng)速度和吞吐量
** 解耦:** 減少服務(wù)之間的影響陷遮,提高系統(tǒng)的穩(wěn)定性和可擴展性。解耦之后可以實現(xiàn)數(shù)據(jù)分發(fā)垦江,生產(chǎn)者發(fā)送一個消息后帽馋,可以由多個消費者來處理
削峰:以穩(wěn)定的系統(tǒng)資源應(yīng)對突發(fā)的流量沖擊
缺點:
系統(tǒng)可用性降低:一旦MQ宕機,整個業(yè)務(wù)就會產(chǎn)生影響(高可用)
系統(tǒng)的復雜度提高:消息丟失比吭,消息重復調(diào)用绽族,消息亂序
數(shù)據(jù)一致性:兩個系統(tǒng)一同處理消息,處理結(jié)果不同梗逮,則數(shù)據(jù)不一致
二项秉、 如何進行產(chǎn)品選型绣溜?
** Kafaka:**
優(yōu)點:吞吐量非常大慷彤,性能非常好,集群高可用
缺點:有可能會丟數(shù)據(jù)怖喻,功能比較單一
使用場景:日志分析底哗,大數(shù)據(jù)采集
RabbitMQ:
優(yōu)點:消息可靠性高,功能全面
缺點:吞吐量比較低锚沸,消息積累會嚴重影響性能跋选,語言不好定制
使用場景:小規(guī)模場景
RocketMQ
優(yōu)點:較高吞吐,較高性能哗蜈,高可用前标,功能非常全面
缺點:開源版功能不如云上商業(yè)版。官方文檔和周邊生態(tài)還不夠成熟距潘。客戶端只支持JAVA
使用場景:幾乎是全場景
三炼列、如何保證消息不丟失?
生產(chǎn)者發(fā)送消息不丟失
Kafka:消息發(fā)送 + 回調(diào)
RocketMQ:消息發(fā)送 + 回調(diào) + 事務(wù)
RabbitMQ:消息發(fā)送 + 回調(diào) + 事務(wù)(channel手動事務(wù)(可能會產(chǎn)生阻塞)) + Publisher Confirm(與RocketMQ事務(wù)機制基本基本相同)
MQ主從消息同步不丟失
RocketMQ:
普通集群:同步同步音比、異步同步俭尖。前者不會丟消息,后者效率更高
Dledger集群-兩階段提交
RabbitMQ:
普通集群:消息分散存儲洞翩,節(jié)點之間不會主動進行消息同步稽犁,可能丟消息
鏡像集群:會在節(jié)點之間主動進行數(shù)據(jù)同步,數(shù)據(jù)安全性得到提高
Kafka:通常用在允許消息少量丟失的場景中
MQ消息存盤不丟失
RocketMQ:同步刷盤骚亿、異步刷盤已亥,前者不會丟消息,后者效率更高
RabbitMQ:隊列配置為持久化隊列来屠。新增Quorum類型的隊列虑椎,采用Raft協(xié)議來進行消息同步
MQ消費者消費消息不丟失
RocketMQ:使用默認的方式消費秫舌,不采用異步
RabbitMQ:關(guān)閉AutoCommit -> 手動提交offset
Kafka:手動提交offset
四、如何保證消息消費的冪等性绣檬?
冪等性:對于同一個系統(tǒng)足陨,在同樣條件下,一次請求和重復多次請求對資源的影響是一致的
消息消費冪等性:防止消費者重復消費消息
所有MQ產(chǎn)品并沒有提供主動解決冪等性的機制娇未,需要有消費者自行控制
RocketMQ:給每個消息分配了個MessageID(不太建議)墨缘,建議使用有業(yè)務(wù)標識的ID
五、如何保證消息的順序零抬?
MQ只需要保證局部有序镊讼,不需要保證全局有序
RocketMQ中有完整的設(shè)計:生產(chǎn)者把一組有序的消息放到同一個隊列當中,消費者一次消費整個隊列中的消息
RabbitMQ:實現(xiàn)上述方式蝶棋。保證目標Exchange只對應(yīng)一個隊列忽妒,一個隊列只對應(yīng)一個消費者
Kafka:實現(xiàn)上述方式。生產(chǎn)者通過Partition分配規(guī)則段直,將消息分配到同一個Partition。Topic下只有一個消費者
六决侈、如何保證消息的高效讀寫?
Kafka和RocketMQ都是通過零拷貝技術(shù)來優(yōu)化文件讀寫
零拷貝:
mmap(JDK中MappedByteBuffer):文件不經(jīng)過用戶空間喧务,直接在內(nèi)核空間完成拷貝功茴。適合比較小的文件
transfile(JDK中FileChannel):文件傳輸過程中直接使用DMA
RocketMQ:mmap(commitlog)
Kafka:在index日志文件中通過mmap方式,使用transfile方式將硬盤數(shù)據(jù)加載到網(wǎng)卡
七肄扎、使用MQ如何保證分布式事務(wù)的最終一致性赁酝?
分布式事務(wù):業(yè)務(wù)相關(guān)的多個操作酌呆,保證其同時成功或失敗
最終一致性:系統(tǒng)中的所有分散在不同節(jié)點的數(shù)據(jù),經(jīng)過一定時間后痰娱,最終能夠達到符合業(yè)務(wù)定義的一致的狀態(tài)梨睁。
保證:
生產(chǎn)者要保證100%的消息投遞
消費者需要保證冪等性消費
八、如何自己設(shè)計一個MQ官辈?
從整體到細節(jié)拳亿,從業(yè)務(wù)場景到技術(shù)實現(xiàn);以RocketMQ為基礎(chǔ)
實現(xiàn)一個單機的隊列數(shù)據(jù)結(jié)構(gòu)愿伴。高效肺魁,可擴展
將單機隊列擴展分布式隊列鹅经。分布式集群管理
基于Topic定制消息路由策略瞬雹。發(fā)送者路由策略刽虹,消費者與隊列對應(yīng)關(guān)系涌哲,消費者路由策略
實現(xiàn)高效的網(wǎng)絡(luò)通信
規(guī)劃日志文件尚镰,實現(xiàn)文件高效讀寫
定制高級功能:死信隊列狗唉,延遲隊列分俯,事務(wù)消息
緩存
一、為什么使用緩存吗铐?
高性能唬渗、高并發(fā)
二、什么是緩存穿透壮啊、緩存擊穿他巨、緩存雪崩减江?怎么解決辈灼?
緩存穿透:數(shù)據(jù)在緩存中查不到巡莹,數(shù)據(jù)庫也查不到
對參數(shù)進行合法性校驗
將數(shù)據(jù)庫沒查到結(jié)果的數(shù)據(jù)也寫入到緩存(短的有效期)
布隆過濾器(存在一定誤判率)降宅,在訪問Redis之前判斷數(shù)據(jù)是否存在
緩存擊穿:緩存單個key失效,去查數(shù)據(jù)庫激才,數(shù)據(jù)庫瞬間壓力增大
設(shè)置熱點數(shù)據(jù)永不過期
加互斥鎖
緩存雪崩:大面積緩存失效额嘿,都去請求數(shù)據(jù)庫,造成數(shù)據(jù)庫短時間內(nèi)承受大量請求而掛掉
將緩存的失效時間分散開(加隨機值)
redis高可用
限流降級
數(shù)據(jù)預熱
三东帅、如何保證Redis與數(shù)據(jù)庫的數(shù)據(jù)一致靠闭?
雙寫模式:先寫數(shù)據(jù)庫愧膀,再寫緩存
失效模式:先寫數(shù)據(jù)庫点弯,在刪除緩存
最終一致性
四抢肛、如何設(shè)計一個分布式鎖?如何對鎖性能進行優(yōu)化?
本質(zhì):在所有進程都能訪問到的一個地方莲镣,設(shè)置一個鎖資源瑞侮,讓這些進程來競爭鎖資源半火。響應(yīng)快钮糖、性能高店归、與業(yè)務(wù)無關(guān)
Redis實現(xiàn)分布式鎖
SETNX k v
EXPIRE k locktime
DEL k
GETSET k v
synchronized單機版鎖消痛。在分布式下不ok
使用redis的SETNX達成分布式鎖
出現(xiàn)異常無法釋放鎖秩伞。在finally中釋放鎖
宕機后质涛,代碼沒走到finally汇陆。設(shè)置鎖的過期時間
SETNX操作和設(shè)置過期時間必須為原子操作
只能刪除自己的鎖毡代,使用lua腳本或redis事務(wù)
判斷鎖所屬業(yè)務(wù)與刪除鎖也必須是原子操作
redis集群環(huán)境下,使用Redisson
五酪耕、Redis如何設(shè)置Key的過期時間迂烁?它的實現(xiàn)原理是什么盟步?Redis默認內(nèi)存大小
EXPIRE SETNX
實現(xiàn)原理(兩種結(jié)合)
定期刪除:默認每隔100ms隨機抽取設(shè)置了過期時間的key却盘,判斷其是否過期黄橘,過期就刪除
惰性刪除:key過期后塞关,下一次用到它時再刪除
沒有配置默認最大內(nèi)存描孟, 生產(chǎn)上推薦設(shè)置為最大物理內(nèi)存的四分之三匿醒。當達到最大內(nèi)存時廉羔,Redis會報OOM
六憋他、海量數(shù)據(jù)下,如何快速查找一條記錄镀娶?
使用布隆過濾器梯码,快速過濾不存在的記錄
在Redis中建立緩存(以Hash來存儲)
七轩娶、五大基本數(shù)據(jù)類型
string
應(yīng)用場景:incr items 商品編號鳄抒、訂單號许溅、是否喜歡
數(shù)據(jù)結(jié)構(gòu):簡單動態(tài)字符串(SDS)闹司。是可以修改的字符串游桩,內(nèi)部結(jié)構(gòu)類似于ArrayList借卧,采用預分配冗余空間的方式來減少內(nèi)存的頻繁分配
list
應(yīng)用場景:微信公眾號
數(shù)據(jù)結(jié)構(gòu):快速鏈表(quicklist)铐刘。在列表元素較少的情況下會使ziplist镰吵,即壓縮列表疤祭。他將所有的元素緊挨著一起存儲勺馆,分配的是一塊連續(xù)的內(nèi)存草穆。數(shù)據(jù)量較多時改為quicklist悲柱。Redis將鏈表和ziplist結(jié)合起來組成quicklist豌鸡。也就是將多個ziplist使用雙向指針串起來直颅。這樣既滿足了快速的插入刪除性能功偿,又不會出現(xiàn)太大的空間冗余
hash:Map<String,Map<Object,Object>>
應(yīng)用場景:購物車早期
數(shù)據(jù)結(jié)構(gòu):與HashMap相似械荷,通過數(shù)組+鏈表的鏈地址法來解決部分哈希沖突吨瞎。hash結(jié)構(gòu)的內(nèi)部包含兩個hashtable颤诀,通常情況下只有一個hashtable是有值的崖叫,但是在字典擴容時需要分配新的hashtable心傀,然后進行漸進式搬遷(rehash)脂男。
漸進式rehash會在rehash同時保留新舊兩個hash結(jié)構(gòu)宰翅,查詢時會同時查詢兩個hash堕油,然后在后續(xù)的定時任務(wù)以及hash操作指令中掉缺,循序漸進的把舊hash的內(nèi)容遷移到新hash中眶明。當搬遷完成后搜囱,新hash取代舊hash
set:HashSet 無序無重復
應(yīng)用場景:微信抽獎小程序蜀肘,微信朋友圈點贊扮宠,抖音共同關(guān)注、可能認識的人(交并差集)
數(shù)據(jù)結(jié)構(gòu):與HashSet相似薄腻,內(nèi)部鍵值對是無序且唯一的庵楷。內(nèi)部實現(xiàn)一個特殊的hash尽纽,其中所有的v指都是null
zset
應(yīng)用場景:商品排序蜓斧,熱搜熱度
數(shù)據(jù)結(jié)構(gòu):數(shù)據(jù)少時挎春,使用ziplist直奋,每個元素都是(數(shù)據(jù)+score)的方式連續(xù)存儲脚线,按照score從小到大排序邮绿。數(shù)據(jù)多時船逮,使用hash+跳表挖胃。hash用來根據(jù)數(shù)據(jù)查score酱鸭,調(diào)表用來根據(jù)score查數(shù)據(jù)凹髓。跳表是基于一條有序單鏈表構(gòu)造的防泵,通過構(gòu)建索引提高查找效率,空間換時間寿谴,查找方式是從最上面的鏈表層層往下查找讶泰,最后在最底層的鏈表找到對應(yīng)節(jié)點
八痪署、LRU算法
1最近最少使用狼犯,常見的頁面置換算法悯森。選擇最近最久未使用的數(shù)據(jù)予以淘汰
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//巧用LinkedHashMap完成
classLRUCache<k,v> extendsLinkedHashMap<k,v> {
????privateintcapacity;
????publicLRUCache(intcapacity) {
????????super(capacity,0.75,true);
????????this.capacity = capacity;
????}
????protectedbooleanremoveEldestEntry(Map.Entry<k,v> eldest){
????????returnsuper.size > capacity;
????}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
publicclassLRUCache {
????/**
?????* 146. LRU 緩存機制
?????* 運用你所掌握的數(shù)據(jù)結(jié)構(gòu),設(shè)計和實現(xiàn)一個? LRU (最近最少使用) 緩存機制 幻碱。
?????* 實現(xiàn) LRUCache 類:
?????* LRUCache(int capacity) 以正整數(shù)作為容量 capacity 初始化 LRU 緩存
?????* int get(int key) 如果關(guān)鍵字 key 存在于緩存中褥傍,則返回關(guān)鍵字的值摔桦,否則返回 -1 邻耕。
?????* void put(int key, int value) 如果關(guān)鍵字已經(jīng)存在兄世,則變更其數(shù)據(jù)值御滩;如果關(guān)鍵字不存在富弦,則???????????????
?????* 插入該組「關(guān)鍵字-值」腕柜。
?????* 當緩存容量達到上限時盏缤,它應(yīng)該在寫入新數(shù)據(jù)之前刪除最久未使用的數(shù)據(jù)值唉铜,從而為新的數(shù)據(jù)值留出???????????
?????* 空間潭流。
?????* 進階:你是否可以在 O(1) 時間復雜度內(nèi)完成這兩種操作幻枉?
?????*/
????//map負責查找,構(gòu)建一個虛擬的雙向鏈表椿肩,里面是一個個Node節(jié)點作為數(shù)據(jù)載體
????//1.構(gòu)造Node節(jié)點作為數(shù)據(jù)載體
????classNode<K, V> {
????????K key;
????????V value;
????????Node<K, V> prev;
????????Node<K, V> next;
????????publicNode() {
????????????this.prev = this.next = null;
????????}
????????publicNode(K key, V value) {
????????????this.key = key;
????????????this.value = value;
????????????this.prev = this.next = null;
????????}
????}
????//2.構(gòu)造一個虛擬的雙向鏈表郑象,里面放Node
????classDoubleLinkedList<K,V>{
????????Node<K,V> head;
????????Node<K,V> tail;
????????//2.1 構(gòu)造方法
????????publicDoubleLinkedList(){
????????????head = newNode<>();
????????????tail = newNode<>();
????????????head.next = tail;?????????????? //頭節(jié)點下一個節(jié)點指向尾節(jié)點
????????????tail.prev = head;?????????????? //尾節(jié)點上一個節(jié)點指向頭節(jié)點
????????}
????????//2.2 添加節(jié)點
????????publicvoidaddHead(Node<K,V> node){
????????????node.next = head.next;????????? //當前節(jié)點下一個節(jié)點指向尾節(jié)點
????????????node.prev = head;?????????????? //當前節(jié)點上一個節(jié)點指向頭節(jié)點
????????????head.next.prev = node;????????? //尾節(jié)點上一個節(jié)點指向當前節(jié)點
????????????head.next = node;?????????????? //頭節(jié)點下一個節(jié)點指向當前節(jié)點
????????}
????????//2.3 刪除節(jié)點
????????publicvoidremove(Node<K,V> node){
????????????node.next.prev = node.prev;???? //尾節(jié)點上一個節(jié)點指向頭節(jié)點
????????????node.prev.next = node.next;???? //頭節(jié)點下一個節(jié)點指向尾節(jié)點
????????????node.prev = null;?????????????? //當前節(jié)點上一個節(jié)點置空
????????????node.next = null;?????????????? //當前節(jié)點下一個節(jié)點置空
????????}
????????//2.4 獲取最后一個節(jié)點
????????publicNode<K,V> getLast(){
????????????returntail.prev;
????????}
????}
????privateintcapacity;
????Map<Integer,Node<Integer,Integer>> map;
????DoubleLinkedList<Integer,Integer> list;
????publicLRUCache(intcapacity) {
????????this.capacity = capacity;
????????map = newHashMap<>();
????????list = newDoubleLinkedList<>();
????}
????publicintget(intkey) {
????????if(!map.containsKey(key)) return-1;
????????Node<Integer, Integer> node = map.get(key);
????????list.remove(node);
????????list.addHead(node);
????????returnnode.value;
????}
????publicvoidput(intkey, intvalue) {
????????if(map.containsKey(key)){
????????????Node<Integer, Integer> node = map.get(key);
????????????node.value = value;
????????????map.put(key,node);
????????????list.remove(node);
????????????list.addHead(node);
????????}else{
????????????if(map.size() == capacity){
????????????????Node<Integer, Integer> lastNode = list.getLast();
????????????????map.remove(lastNode.key);
????????????????list.remove(lastNode);
????????????}
????????????Node<Integer, Integer> newNode = newNode<>(key, value);
????????????map.put(key,newNode);
????????????list.addHead(newNode);
????????}
????}
}
微服務(wù)篇
一丽惭、談?wù)勀銓ξ⒎?wù)的理解责掏,微服務(wù)有哪些優(yōu)缺點换衬?
微服務(wù)是一種架構(gòu)風格,通過將單體的應(yīng)用劃分為較小的服務(wù)單元废士,從而降低整個系統(tǒng)的復雜度
優(yōu)點:
服務(wù)部署更靈活:每個應(yīng)用都可以是一個獨立的項目,可以獨立部署,耦合性低
技術(shù)更新靈活:微服務(wù)可以根據(jù)業(yè)務(wù)特點忿危,靈活選擇技術(shù)棧
應(yīng)用性能高:一個應(yīng)用可以部署到多個服務(wù)器
代碼復用:很多底層服務(wù)可以以REST API的方式對外提供統(tǒng)一的服務(wù)铺厨,所以基礎(chǔ)服務(wù)可以在整個微服務(wù)中通用
缺點:
服務(wù)調(diào)用的復雜性提高:網(wǎng)絡(luò)問題解滓,容錯問題洼裤,負載問題,高并發(fā)問題
分布式事務(wù):盡量不要使用
測試難度提升
運維難度提升
二移国、SpringCloud和SpringCloudAlibaba都有哪些組件迹缀?都解決了什么問題祝懂?
SpringCloud:是提供構(gòu)建微服務(wù)系統(tǒng)多需要的一組通用開發(fā)模式以及一系列快速實現(xiàn)這些開發(fā)模式的工具
Eureka:服務(wù)注冊與發(fā)現(xiàn)
Hytrix:服務(wù)熔斷降級
Feign - Ribbon:遠程服務(wù)調(diào)用
OpenFeign:遠程服務(wù)調(diào)用
Zuul:網(wǎng)關(guān)路由
Config:配置中心
SpringCloudAlibaba:
Sentinel:服務(wù)熔斷降級
Nacos:注冊中心和配置中心
Stream RocketMQ:消息中間件整合
Seata:分布式事務(wù)
三嫂易、分布式事務(wù)如何處理颅和?怎么保證事務(wù)一致性峡扩?
分布式事務(wù):不同節(jié)點的實務(wù)操作提供操作原子性保證教届。在原本沒有直接關(guān)聯(lián)的事務(wù)之間建立聯(lián)系案训,
HTTP:最大努力通知,事后補償
MQ:事務(wù)消息機制
Seata:通過TC來在多個事務(wù)之間建立聯(lián)系
兩階段:AT XA (鎖資源)
三階段:TCC (在兩階段前增加一個準備階段粪糙,不鎖資源)
SAGA:類似于熔斷强霎,業(yè)務(wù)自己實現(xiàn)正向操作和補償操作的邏輯
四、怎么拆分微服務(wù)蓉冈?怎么樣設(shè)計出高內(nèi)聚,低耦合的微服務(wù)寞酿?有沒有了解過DDD領(lǐng)域驅(qū)動設(shè)計家夺?什么是中臺?中臺和微服務(wù)有什么關(guān)系伐弹?
拆分微服務(wù)的基本準則:
微服務(wù)之間盡量不要有業(yè)務(wù)交叉
微服務(wù)之間只能通過接口進行調(diào)用
高內(nèi)聚拉馋,低耦合
高內(nèi)聚低耦合是一種從上到下指導微服務(wù)設(shè)計的方法,實現(xiàn)其工具主要有同步的接口調(diào)用和異步的事件驅(qū)動
DDD領(lǐng)域驅(qū)動設(shè)計是一種通過將實現(xiàn)連接到持續(xù)進化的模型來滿足復雜需求的軟件開發(fā)方法掸茅。
中臺:將各個業(yè)務(wù)線中可以復用的一些功能抽取出來椅邓,剝離個性,提取共性昧狮,形成一些可復用的組件
業(yè)務(wù)中臺景馁、數(shù)據(jù)中臺、技術(shù)中臺
五逗鸣、你的項目中是怎么保證微服務(wù)敏捷開發(fā)的合住?微服務(wù)的鏈路追蹤、持續(xù)集成撒璧、AB發(fā)布要怎么做?
敏捷開發(fā):為了提高團隊的交付效率透葛,快速迭代,快速試錯
鏈路追蹤:基于日志卿樱,形成全局事務(wù)ID僚害,落地到日志文件
持續(xù)集成:maven pom -> build ->shell
AB發(fā)布:
藍綠發(fā)布,紅黑發(fā)布繁调。新老版本同時存在
灰度發(fā)布
Spring篇
一萨蚕、Spring框架中Bean的創(chuàng)建過程是怎么樣的靶草?
實例化、屬性賦值岳遥、初始化奕翔、銷毀
實例化
當客戶端容器申請一個Bean時
當容器在初始化一個Bean時發(fā)現(xiàn)還需要依賴另一個Bean時
以BeanDefinition對象的形式保存
屬性賦值(依賴注入):Spring通過BeanDefinition找到對象依賴的其他對象,并將這些對象賦予當前對象
處理Aware接口:Spring會檢測對象是否實現(xiàn)了xxxAware接口浩蓉,實現(xiàn)了則調(diào)用相應(yīng)的方法
BeanPostProcessor前置處理:調(diào)用BeanPostProcessor的postProcessBeforeInitialization()
InitializationBean:Spring檢測對象如果實現(xiàn)此接口派继,就會執(zhí)行它的afterPropertiesSet()方法,定制初始化邏輯
init-method:Spring發(fā)現(xiàn)Bean配置了這個屬性捻艳,就會調(diào)用它的配置方法驾窟,執(zhí)行它的初始化邏輯 @PostConStruct
BeanPostProcessor后置處理:調(diào)用BeanPostProcessor的postProcessAfterInitialization()
·····Bean創(chuàng)建完成·····
DisposableBean:如果Bean實現(xiàn)此接口,在對象銷毀前會調(diào)用destory()方法
destory-method:@PreDestory
二认轨、Spring中Bean是線程安全的嗎纫普?如果不安全,要如何處理好渠?
不是
SpringBean作用域:
singleton:單例
prototype:為每個Bean請求創(chuàng)建實例
request:為每一個request請求創(chuàng)建一個實例,請求完成后失效
session:為每一個session請求創(chuàng)建一個實例节视,請求完成后失效
global-session:全局
singleton:默認線程不安全拳锚。大部分Bean無狀態(tài),不需要保證線程安全
prototype:每次都生成一個新的對象寻行,所以不存在線程安全問題
三霍掺、Spring如何處理循環(huán)依賴問題?
循環(huán)依賴:多個Bean之間相互依賴拌蜘,形成了一個閉環(huán)杆烁。A依賴B,B依賴C厚掷,C依賴A
Spring中singleton支持循環(huán)依賴续誉,Prototype不支持弛作。只有單例的Bean會通過三級緩存提前暴露來解決循環(huán)依賴問題,而非單例Bean每次都是從容器中獲取一個新的對象析校,都會重新創(chuàng)建,所以 非單例的Bean是沒有緩存的铜涉,不會將其放到三級緩存中
三級緩存
第一級緩存(單例池):singletonObjects智玻,存放已經(jīng)經(jīng)歷了完整生命周期的Bean對象
第二級緩存:earlySingletonObjects,存放早期暴露出來的Bean對象芙代,Bean的生命周期未結(jié)束(屬性還未填充完)
第三級緩存:Map> singletonFactorices吊奢,存放可以生成Bean的工廠
三級緩存的遷移過程
A創(chuàng)建過程中需要注入屬性B,于是將A放入三級緩存纹烹,去創(chuàng)建B
B創(chuàng)建過程中發(fā)現(xiàn)需要注入屬性A,于是B先查一級緩存页滚,沒有再查二級緩存召边,沒有再查三級緩存,在三級緩存中找到A逻谦,然后將三級緩存的A放入二級緩存并刪除三級緩存的A
B創(chuàng)建完畢掌实,將自己放入一級緩存(此時B的屬性A的狀態(tài)依然是創(chuàng)建中),然后繼續(xù)創(chuàng)建A邦马,從一級緩存里拿到B贱鼻,完成A的創(chuàng)建,將A放入一級緩存
四滋将、Spring如何處理事務(wù)
編程式事務(wù)
聲明式事務(wù):Spring在AOP基礎(chǔ)上提供的事務(wù)實現(xiàn)機制
傳播級別
PROPAGATION_REQUIRED:默認傳播行為邻悬。如果當前沒有事務(wù),就創(chuàng)建一個新事務(wù)随闽,如果當前存在事務(wù)父丰,就加入到事務(wù)中。
PROPAGATION_SUPPORTS:如果當前存在事務(wù)掘宪,就加入到該事務(wù)蛾扇。如果當前不存在事務(wù),就以非事務(wù)方式運行。
PROPAGATION_MANDATORY:如果當前存在事務(wù)魏滚,就加入該事務(wù)镀首。如果當前不存在事務(wù)就拋出異常。
PROPAGATION_REQUIRES_NEW:無論當前存不存在事務(wù)鼠次,都創(chuàng)建新事務(wù)進行執(zhí)行更哄。
PROPAGATION_NOT_SUPPORTED:以非事務(wù)方式運行。如果當前存在事務(wù)腥寇,就將當前事務(wù)掛起成翩。
PROPAGATION_NEVER:以非事務(wù)方式運行。如果當前存在事務(wù)赦役,就拋出異常麻敌。
PROPAGATION_NESTED:如果當前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行;如果當前沒有事務(wù)則按REQUEIRED屬性執(zhí)行扩劝。
@Transactional注解的失效場景
@Transactional 應(yīng)用在非 public 修飾的方法上:事務(wù)攔截器中會檢查目標方法的修飾符庸论,不是public不會獲取@Transactional的屬性配置信息
@Transactional 注解屬性 propagation 設(shè)置錯誤
@Transactional 注解屬性 rollbackFor 設(shè)置錯誤
同一個類中方法調(diào)用,導致@Transactional失效
異常被catch"吃了"導致@Transactional失效
數(shù)據(jù)庫引擎不支持事務(wù)
隔離級別
ISOLATION_DEFAULT:使用數(shù)據(jù)庫默認的事務(wù)隔離級別棒呛。
ISOLATION_READ_UNCOMMITTED:讀未提交聂示。允許事務(wù)在執(zhí)行過程中,讀取其他事務(wù)未提交的數(shù)據(jù)簇秒。
ISOLATION_READ_COMMITTED:讀已提交鱼喉。允許事務(wù)在執(zhí)行過程中,讀取其他事務(wù)已經(jīng)提交的數(shù)據(jù)。
ISOLATION_REPEATABLE_READ:可重復讀扛禽。在同一個事務(wù)內(nèi)锋边,任意時刻的查詢結(jié)果是一致的。
ISOLATION_SERIALIZABLE:所有事務(wù)依次執(zhí)行编曼。
五豆巨、SpringMVC中的控制器是不是單例模式?如果是,如何保證線程安全?
是
單例模式下就會有線程安全問題
Spring中保證線程安全的方法
將scop設(shè)置成非singleton:prototype掐场,request
將控制器設(shè)計成無狀態(tài)模式往扔。在controller中不要攜帶數(shù)據(jù),但是可以引用無狀態(tài)的service和dao
六熊户、Spring AOP順序
正常
環(huán)繞通知前
@Before
被調(diào)用方法
@AfterReturning
@After
環(huán)繞通知后
異常
環(huán)繞通知前
@Before
被調(diào)用方法
@AfterThrowing
@After
Linux篇
一萍膛、生產(chǎn)環(huán)境服務(wù)器變慢,診斷思路和性能評估嚷堡?
整機:top(精簡版uptime)
load average(系統(tǒng)負載均衡):1蝗罗,5,15系統(tǒng)平均負載值蝌戒,三個值相加除以3高于60%串塑,系統(tǒng)壓力重
CPU:vmstat
內(nèi)存:free -m
硬盤:df -h
磁盤IO:iostat -xdk
網(wǎng)絡(luò)IO:ifstat
二、生產(chǎn)環(huán)境出現(xiàn)CPU占用過高北苟,分析思路和定位拟赊?
先用top命令找出CPU占比最高的
ps -ef或jps進一步定位
定位到具體線程或者代碼
ps -mp 進程ID -o THREAD,tid,time
將需要的線程ID轉(zhuǎn)換為16進制格式(英文小寫)
jstack進程ID|grep tid(16進制線程ID英文小寫) -A60
MySql篇
一、索引
索引是幫助Sql高效獲取數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)粹淋。索引存儲在文件系統(tǒng)中。索引的文件存儲形式與存儲引擎(InnoDB瑟慈、MyISAM)有關(guān)桃移。索引文件的結(jié)構(gòu)有hash、二叉樹葛碧、B樹借杰、B+樹
MySql索引類型
主鍵索引
唯一索引:索引列的所有值都只能出現(xiàn)一次,必須唯一进泼,值可以為空
普通索引:基本索引類型蔗衡,值可以為空,沒有唯一性的限制
全文索引:索引類型為FILLTEXT乳绕〗实耄可以在varchar、char洋措、text類型的列上創(chuàng)建
組合索引:多列值組成一個索引济蝉,用于組合搜索
為什么要用B+樹:
hash缺點:比較耗費內(nèi)存空間,不適合范圍查找
二叉樹、紅黑樹缺點:會由于樹的深度過深而造成io次數(shù)變多王滤,影響讀取效率
B樹缺點:不能存取較大的數(shù)據(jù)
名詞解釋
回表:從索引中查出主鍵贺嫂,再用主鍵去查索引中沒有的數(shù)據(jù)
覆蓋索引:從索引中就可以查到,不需要回表
最左匹配:在組合索引中雁乡,如果sql語句中用到了組合索引中的最左邊的索引第喳,那么這條sql語句就可以利用這個組合索引去進行匹配。當遇到范圍查詢(>踱稍、<曲饱、between、like)就會停止匹配
索引下推:在最左匹配原則的基礎(chǔ)上寞射,直接按照條件查詢的所有條件做判斷渔工,并從存儲引擎中拉去符合條件的數(shù)據(jù)
索引優(yōu)化
當使用索引進行查詢的時候盡量不要使用表達式,把計算放到業(yè)務(wù)層而不是數(shù)據(jù)庫層
盡量使用主鍵查詢桥温,而不是其他索引引矩,因為主鍵查詢不會觸發(fā)會標查詢
使用前綴索引
使用索引掃描來排序
union all、in侵浸、or都能夠使用索引旺韭,但是推薦用in
范圍列可以用到索引
強制類型轉(zhuǎn)換會全表掃描
更新十分頻繁,數(shù)據(jù)區(qū)分度不高的字段上不宜建立索引
創(chuàng)建索引的列掏觉,不允許為null区端,可能會得到不符合預期的效果
最好不要超過三張表連接,因為需要join的字段澳腹,數(shù)據(jù)類型必須一致
能用limit盡量用limit
單表索引建議控制在5個以內(nèi)
組合索引時织盼,但索引字段數(shù)不允許超過5個
索引不是越多越好
不要在不了解系統(tǒng)的情況下進行過早優(yōu)化
like、不等于酱塔、大于小于走不走索引沥邻?
like語句要使索引生效,like后不能以%開始羊娃,也就是說 (like %字段名%) 唐全、(like %字段名)這類語句會使索引失效,而(like 字段名)蕊玷、(like 字段名%)這類語句索引是可以正常使用
在使用不等于(S世=或者<>)的時候無法使用索引導致全表掃描 is not null也無法使用索引,但是is null是可以使用索引的.
聚簇索引和非聚簇索引
聚簇索引的B+樹葉子節(jié)點的data是完整的數(shù)據(jù)記錄
非聚簇索引的B+樹葉子節(jié)點的data是主鍵的值
二垃帅、事務(wù)
原子性A:要么全部成功延届,要么全部失敗
一致性C:所有事務(wù)讀取同一個數(shù)據(jù)的結(jié)果是相同的
隔離性I:一個事務(wù)在提交之前,對其他事務(wù)不可見
持久性D:一旦事務(wù)提交贸诚,所做的操作永久保存在數(shù)據(jù)庫中
三祷愉、存儲引擎
InnoDB:默認隔離級別可重復讀
解決幻讀:MVCC+Next-key Locking
MVCC:寫操作更新最新的版本快照窗宦,讀操作讀舊版本快照,快照存儲在Undo日志中
Next-key Locking:鎖定一個記錄上的索引和索引之間的間隙二鳄。鎖定一個前開后閉區(qū)間赴涵。一個索引包含:10,11订讼,13髓窜,20。鎖定的區(qū)間為(-∞,10](10,11](11,13](13,20](20,+∞)
MyISAM