一趾代、Java 基礎(chǔ)知識
1唠叛、Object 類相關(guān)方法
getClass
獲取當(dāng)前運行時對象的 Class 對象臀晃。
hashCode
返回對象的 hash 碼野瘦。
clone
拷貝當(dāng)前對象, 必須實現(xiàn) Cloneable 接口切心。淺拷貝對基本類型進行值拷貝朽寞,對引用類型拷貝引用挂洛;深拷貝對基本類型進行值拷貝黎比,對引用類型對象不但拷貝對象的引用還拷貝對象的相關(guān)屬性和方法超营。兩者不同在于深拷貝創(chuàng)建了一個新的對象。
equals
通過內(nèi)存地址比較兩個對象是否相等阅虫,String 類重寫了這個方法使用值來比較是否相等演闭。
toString
返回類名@哈希碼的 16 進制。
notify
喚醒當(dāng)前對象監(jiān)視器的任一個線程颓帝。
notifyAll
喚醒當(dāng)前對象監(jiān)視器上的所有線程米碰。
wait
1、暫停線程的執(zhí)行躲履;2、三個不同參數(shù)方法(等待多少毫秒聊闯;額外等待多少毫秒工猜;一直等待)3、與Thread.sleep(long time)相比菱蔬,sleep 使當(dāng)前線程休眠一段時間篷帅,并沒有釋放該對象的鎖史侣,wait 釋放了鎖。
finalize
對象被垃圾回收器回收時執(zhí)行的方法魏身。
2惊橱、基本數(shù)據(jù)類型
整型:byte(8)、short(16)箭昵、int(32)税朴、long(64)
浮點型:float(32)、double(64)
布爾型:boolean(8)
字符型:char(16)
3家制、序列化
Java 對象實現(xiàn)序列化要實現(xiàn) Serializable 接口正林。
反序列化并不會調(diào)用構(gòu)造方法。反序列的對象是由 JVM 自己生成的對象颤殴,不通過構(gòu)造方法生成觅廓。
序列化對象的引用類型成員變量,也必須是可序列化的涵但,否則杈绸,會報錯。
如果想讓某個變量不被序列化矮瘟,使用 transient 修飾瞳脓。
單例類序列化,需要重寫 readResolve() 方法芥永。
4篡殷、String、StringBuffer埋涧、StringBuilder
String
由 char[] 數(shù)組構(gòu)成板辽,使用了 final 修飾,是不可變對象棘催,可以理解為常量劲弦,線程安全;對 String 進行改變時每次都會新生成一個 String 對象醇坝,然后把指針指向新的引用對象邑跪。
StringBuffer 線程安全;StringBuiler 線程不安全呼猪。
操作少量字符數(shù)據(jù)用 String画畅;單線程操作大量數(shù)據(jù)用 StringBuilder;多線程操作大量數(shù)據(jù)用 StringBuffer宋距。
5轴踱、重載與重寫
重載
發(fā)生在同一個類中,方法名相同谚赎,參數(shù)的類型淫僻、個數(shù)诱篷、順序不同,方法的返回值和修飾符可以不同雳灵。
重寫
發(fā)生在父子類中棕所,方法名和參數(shù)相同,返回值范圍小于等于父類悯辙,拋出的異常范圍小于等于父類琳省,訪問修飾符范圍大于等于父類;如果父類方法訪問修飾符為 private 或者 final 則子類就不能重寫該方法笑撞。
6岛啸、final
修飾基本類型變量,一經(jīng)出初始化后就不能夠?qū)ζ溥M行修改茴肥。
修飾引用類型變量坚踩,不能夠指向另一個引用。
修飾類或方法瓤狐,不能被繼承或重寫瞬铸。
7、反射
在運行時動態(tài)的獲取類的完整信息
增加程序的靈活性
JDK 動態(tài)代理使用了反射
8础锐、JDK 動態(tài)代理
使用步驟
創(chuàng)建接口及實現(xiàn)類
實現(xiàn)代理處理器:實現(xiàn) InvokationHandler 嗓节,實現(xiàn) invoke(Proxy proxy,Method method皆警,Object[] args) 方法
通過 Proxy.newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h) 獲得代理類
通過代理類調(diào)用方法拦宣。
9、Java IO
普通 IO 信姓,面向流鸵隧,同步阻塞線程。
NIO意推,面向緩沖區(qū)豆瘫,同步非阻塞。
二菊值、Java 集合框架
1外驱、List(線性結(jié)構(gòu))
ArrayList
Object[] 數(shù)組實現(xiàn),默認(rèn)大小為 10 腻窒,支持隨機訪問昵宇,連續(xù)內(nèi)存空間,插入末尾時間復(fù)雜度 o(1)儿子,插入第 i 個位置時間復(fù)雜度 o(n - i)瓦哎。擴容,大小變?yōu)?b>1.5倍,Arrays.copyOf(底層 System.ArrayCopy)杭煎,復(fù)制到新數(shù)組,指針指向新數(shù)組卒落。
Vector
類似 ArrayList羡铲,線程安全,擴容默認(rèn)增長為原來的2倍儡毕,還可以指定增長空間長度也切。
LinkedList
基于鏈表實現(xiàn),1.7 為雙向鏈表腰湾,1.6 為雙向循環(huán)鏈表雷恃,取消循環(huán)更能分清頭尾。
2费坊、Map(K倒槐,V 對)
HashMap
底層數(shù)據(jù)結(jié)構(gòu),JDK 1.8 是數(shù)組 + 鏈表 + 紅黑樹附井,JDK 1.7 無紅黑樹讨越。鏈表長度大于 8 時,轉(zhuǎn)化為紅黑樹永毅,優(yōu)化查詢效率把跨。
初始容量為16,通過 tableSizeFor 保證容量為 2 的冪次方沼死。尋址方式着逐,高位異或,(n-1)&h取模意蛀,優(yōu)化速度耸别。
擴容機制,當(dāng)元素數(shù)量大于容量 x 負(fù)載因子 0.75 時浸间,容量擴大為原來的 2 倍太雨,新建一個數(shù)組,然后轉(zhuǎn)移到新數(shù)組魁蒜。
基于 Map 實現(xiàn)囊扳。
線程不安全。
HashMap (1.7) 多線程循環(huán)鏈表問題
在多線程環(huán)境下兜看,進行擴容時锥咸,1.7 下的 HashMap 會形成循環(huán)鏈表。
怎么形成循環(huán)鏈表:
假設(shè)有一 HashMap 容量為 2 细移, 在數(shù)組下標(biāo) 1 位置以 A -> B 鏈表形式存儲搏予。有一線程對該 map 做 put 操作,由于觸發(fā)擴容條件弧轧,需要進行擴容雪侥。這時另一個線程也 put 操作碗殷,同樣需要擴容,并完成了擴容操作速缨,由于復(fù)制到新數(shù)組是頭部插入锌妻,所以 1 位置變?yōu)?B -> A 。這時第一個線程繼續(xù)做擴容操作旬牲,首先復(fù)制 A 仿粹,然后復(fù)制 B ,再判斷 B.next 是否為空時原茅,由于第二個線程做了擴容操作吭历,導(dǎo)致 B.next = A,所以在將 A 放到 B 前擂橘,A.next 又等于 B 晌区,導(dǎo)致循環(huán)鏈表出現(xiàn)。
HashTable
線程安全通贞,方法基本全用 Synchronized 修飾契讲。
初始容量為 11 ,擴容為 2n + 1 滑频。
繼承Dictionary類捡偏。
ConcurrentHashMap
線程安全的 HashMap。
1.7 采用分段鎖的形式加鎖峡迷;1.8 使用 Synchronized 和 CAS 實現(xiàn)同步银伟,若數(shù)組的 Node 為空,則通過 CAS 的方式設(shè)置值绘搞,不為空則加在鏈表的第一個節(jié)點彤避。獲取第一個元素是否為空使用 Unsafe 類提供的 getObjectVolatile 保證可見性。
對于讀操作夯辖,數(shù)組由 volatile 修飾琉预,同時數(shù)組的元素為 Node,Node 的 K 使用 final 修飾蒿褂,V 使用 volatile 修飾圆米,下一個節(jié)點也用 volatile 修飾,保證多線程的可見性啄栓。
LinkedHashMap
LinkedHashMap 繼承自 HashMap娄帖,所以它的底層仍然是基于拉鏈?zhǔn)缴⒘薪Y(jié)構(gòu)即由數(shù)組和鏈表或紅黑樹組成。另外昙楚,LinkedHashMap 在上面結(jié)構(gòu)的基礎(chǔ)上近速,增加了一條雙向鏈表,使得上面的結(jié)構(gòu)可以保持鍵值對的插入順序。
TreeMap
有序的 Map削葱,紅黑樹結(jié)構(gòu)奖亚,可以自定義比較器來進行排序。
Collections.synchronizedMap 如何實現(xiàn) Map 線程安全析砸?
基于 Synchronized 遂蛀,實際上就是鎖住了當(dāng)前傳入的 Map 對象。
3干厚、Set(唯一值)
HashSet
基于 HashMap 實現(xiàn),使用了 HashMap 的 K 作為元素存儲螃宙,V 為 new Object() 蛮瞄,在 add() 方法中如果兩個元素的 Hash 值相同,則通過 equals 方法比較是否相等谆扎。
LinkedHashSet
LinkedHashSet 繼承于 HashSet挂捅,并且其內(nèi)部是通過 LinkedHashMap 來實現(xiàn)的。
TreeSet
紅黑樹實現(xiàn)有序唯一堂湖。
三闲先、Java 多線程
1、synchronized
修飾代碼塊
底層實現(xiàn)无蜂,通過 monitorenter & monitorexit 標(biāo)志代碼塊為同步代碼塊伺糠。
修飾方法
底層實現(xiàn),通過 ACC_SYNCHRONIZED 標(biāo)志方法是同步方法斥季。
修飾類 class 對象時训桶,實際鎖在類的實例上面。
單例模式
publicclassSingleton{privatestaticvolatileSingletoninstance=null;privateSingleton(){}publicstaticSingletongetInstance(){if(null==instance){synchronized(Singleton.class){if(null==instance){instance=newSingleton();}}}returninstance;}}
偏向鎖酣倾,自旋鎖舵揭,輕量級鎖,重量級鎖
通過 synchronized 加鎖躁锡,第一個線程獲取的鎖為偏向鎖午绳,這時有其他線程參與鎖競爭,升級為輕量級鎖映之,其他線程通過循環(huán)的方式嘗試獲得鎖拦焚,稱自旋鎖。若果自旋的次數(shù)達到一定的閾值杠输,則升級為重量級鎖耕漱。
需要注意的是,在第二個線程獲取鎖時抬伺,會先判斷第一個線程是否仍然存活螟够,如果不存活,不會升級為輕量級鎖。
2妓笙、Lock
ReentrantLock
基于 AQS (AbstractQueuedSynchronizer)實現(xiàn)若河,主要有 state (資源) + FIFO (線程等待隊列) 組成。
公平鎖與非公平鎖:區(qū)別在于在獲取鎖時寞宫,公平鎖會判斷當(dāng)前隊列是否有正在等待的線程萧福,如果有則進行排隊。
使用 lock() 和 unLock() 方法來加鎖解鎖辈赋。
ReentrantReadWriteLock
同樣基于 AQS 實現(xiàn)鲫忍,內(nèi)部采用內(nèi)部類的形式實現(xiàn)了讀鎖(共享鎖)和寫鎖 (排它鎖)。
非公平鎖吞吐量高
在獲取鎖的階段來分析钥屈,當(dāng)某一線程要獲取鎖時悟民,非公平鎖可以直接嘗試獲取鎖,而不是判斷當(dāng)前隊列中是否有線程在等待篷就。一定情況下可以避免線程頻繁的上下文切換射亏,這樣,活躍的線程有可能獲得鎖竭业,而在隊列中的鎖還要進行喚醒才能繼續(xù)嘗試獲取鎖智润,而且線程的執(zhí)行順序一般來說不影響程序的運行。
3未辆、volatile
Java 內(nèi)存模型
image.png
在多線程環(huán)境下窟绷,保證變量的可見性。使用了 volatile 修飾變量后咐柜,在變量修改后會立即同步到主存中钾麸,每次用這個變量前會從主存刷新。
禁止 JVM 指令重排序炕桨。
單例模式雙重校驗鎖變量為什么使用 volatile 修飾饭尝?
禁止 JVM 指令重排序,new Object()分為三個步驟:申請內(nèi)存空間献宫,將內(nèi)存空間引用賦值給變量钥平,變量初始化。如果不禁止重排序姊途,有可能得到一個未經(jīng)初始化的變量涉瘾。
4、線程的五種狀態(tài)
1). New
一個新的線程被創(chuàng)建捷兰,還沒開始運行立叛。
2). Runnable
一個線程準(zhǔn)備就緒,隨時可以運行的時候就進入了 Runnable 狀態(tài)贡茅。
Runnable 狀態(tài)可以是實際正在運行的線程秘蛇,也可以是隨時可以運行的線程其做。
多線程環(huán)境下,每個線程都會被分配一個固定長度的 CPU 計算時間赁还,每個線程運行一會兒就會停止讓其他線程運行妖泄,這樣才能讓每個線程公平的運行。這些等待 CPU 和正在運行的線程就處于 Runnable 狀態(tài)艘策。
3). Blocked
例如一個線程在等待 I/O 資源蹈胡,或者它要訪問的被保護代碼已經(jīng)被其他線程鎖住了,那么它就在阻塞 Blocked 狀態(tài)朋蔫,這個線程所需的資源到位后就轉(zhuǎn)入 Runnable 狀態(tài)罚渐。
4). Waiting(無限期等待)
如果一個線程在等待其他線程的喚醒,那么它就處于 Waiting 狀態(tài)驯妄。以下方法會讓線程進入等待狀態(tài):
Object.wait()
Thread.join()
LockSupport.park()
5). Timed Waiting(有期限等待)
無需等待被其他線程顯示喚醒荷并,在一定時間后有系統(tǒng)自動喚醒。
以下方法會讓線程進入有限等待狀態(tài):
Thread.sleep(sleeptime)
Object.wait(timeout)
Thread.join(timeout)
LockSupport.parkNanos(timeout)
LockSupport.parkUntil(timeout)
6). Terminated
一個線程正常執(zhí)行完畢富玷,或者意外失敗,那么就結(jié)束了既穆。
5赎懦、 wait() 與 sleep()
調(diào)用后線程進入 waiting 狀態(tài)。
wait() 釋放鎖幻工,sleep() 沒有釋放鎖励两。
調(diào)用 wait() 后需要調(diào)用 notify() 或 notifyAll() 方法喚醒線程。
wait() 方法聲明在 Object 中囊颅,sleep() 方法聲明在 Thread 中当悔。
6、 yield()
調(diào)用后線程進入 runnable 狀態(tài)踢代。
讓出 CPU 時間片盲憎,之后有可能其他線程獲得執(zhí)行權(quán),也有可能這個線程繼續(xù)執(zhí)行胳挎。
7饼疙、 join()
在線程 B 中調(diào)用了線程 A 的 Join()方法,直到線程 A 執(zhí)行完畢后慕爬,才會繼續(xù)執(zhí)行線程 B窑眯。
可以保證線程的順序執(zhí)行。
join() 方法必須在 線程啟動后調(diào)用才有意義医窿。
使用 wait() 方法實現(xiàn)磅甩。
9、線程使用方式
繼承 Tread 類
實現(xiàn) Runnable 接口
實現(xiàn) Callable 接口:帶有返回值
10姥卢、Runnable 和 Callable 比較
方法簽名不同卷要,void Runnable.run(),V Callable.call() throws Exception
是否允許有返回值渣聚,Callable允許有返回值
是否允許拋出異常,Callable允許拋出異常却妨。
提交任務(wù)方式饵逐,Callable使用Future<T> submit(Callable<T> task)返回 Future 對象,調(diào)用其 get() 方法可以獲得返回值彪标,Runnable使用void execute(Runnable command)倍权。
11、hapens-before
如果一個操作 happens-before 另一個操作捞烟,那么第一個操作的執(zhí)行結(jié)果將對第二個操作可見薄声,而且第一個操作的執(zhí)行順序排在第二個操作之前。
12题画、ThreadLocal
場景
主要用途是為了保持線程自身對象和避免參數(shù)傳遞默辨,主要適用場景是按線程多實例(每個線程對應(yīng)一個實例)的對象的訪問,并且這個對象很多地方都要用到苍息。
原理
為每個線程創(chuàng)建變量副本缩幸,不同線程之間不可見,保證線程安全竞思。使用 ThreadLocalMap 存儲變量副本表谊,以 ThreadLocal 為 K,這樣一個線程可以擁有多個 ThreadLocal 對象盖喷。
實際
使用多數(shù)據(jù)源時爆办,需要根據(jù)數(shù)據(jù)源的名字切換數(shù)據(jù)源,假設(shè)一個線程設(shè)置了一個數(shù)據(jù)源课梳,這個時候就有可能有另一個線程去修改數(shù)據(jù)源距辆,可以使用 ThreadLocal 維護這個數(shù)據(jù)源名字,使每個線程持有數(shù)據(jù)源名字的副本暮刃,避免線程安全問題跨算。
8、線程池
1)椭懊、分類
FixThreadPool 固定數(shù)量的線程池漂彤,適用于對線程管理,高負(fù)載的系統(tǒng)
SingleThreadPool 只有一個線程的線程池灾搏,適用于保證任務(wù)順序執(zhí)行
CacheThreadPool 創(chuàng)建一個不限制線程數(shù)量的線程池挫望,適用于執(zhí)行短期異步任務(wù)的小程序,低負(fù)載系統(tǒng)
ScheduledThreadPool 定時任務(wù)使用的線程池狂窑,適用于定時任務(wù)
2)媳板、線程池的幾個重要參數(shù)
int corePoolSize, 核心線程數(shù)
int maximumPoolSize, 最大線程數(shù)
long keepAliveTime, TimeUnit unit, 超過 corePoolSize 的線程的存活時長,超過這個時間泉哈,多余的線程會被回收蛉幸。
BlockingQueue<Runnable> workQueue,? 任務(wù)的排隊隊列
ThreadFactory threadFactory,? 新線程的產(chǎn)生方式
RejectedExecutionHandler handler)? 拒絕策略
3)破讨、線程池線程工作過程
corePoolSize -> 任務(wù)隊列 -> maximumPoolSize -> 拒絕策略
核心線程在線程池中一直存活,當(dāng)有任務(wù)需要執(zhí)行時奕纫,直接使用核心線程執(zhí)行任務(wù)提陶。當(dāng)任務(wù)數(shù)量大于核心線程數(shù)時,加入等待隊列匹层。當(dāng)任務(wù)隊列數(shù)量達到隊列最大長度時隙笆,繼續(xù)創(chuàng)建線程,最多達到最大線程數(shù)升筏。當(dāng)設(shè)置回收時間時撑柔,核心線程以外的空閑線程會被回收。如果達到了最大線程數(shù)還不能夠滿足任務(wù)執(zhí)行需求您访,則根據(jù)拒絕策略做拒絕處理铅忿。
4)、線程池拒絕策略(默認(rèn)拋出異常)
|:---|:---|
| AbortPolicy |? 拋出 RejectedExecutionException |
| DiscardPolicy |? 什么也不做灵汪,直接忽略 |
| DiscardOldestPolicy |? 丟棄執(zhí)行隊列中最老的任務(wù)檀训,嘗試為當(dāng)前提交的任務(wù)騰出位置 |
| CallerRunsPolicy |? 直接由提交任務(wù)者執(zhí)行這個任務(wù) |
5)、如何根據(jù) CPU 核心數(shù)設(shè)計線程池線程數(shù)量
IO 密集型 2nCPU
計算密集型 nCPU+1
其中 n 為 CPU 核心數(shù)量享言,可通過Runtime.getRuntime().availableProcessors()獲得核心數(shù):峻凫。
為什么加 1:即使當(dāng)計算密集型的線程偶爾由于缺失故障或者其他原因而暫停時,這個額外的線程也能確保 CPU 的時鐘周期不會被浪費担锤。
四蔚晨、Java 虛擬機
1乍钻、Java 內(nèi)存結(jié)構(gòu)
image.png
堆
由線程共享肛循,存放 new 出來的對象,是垃圾回收器的主要工作區(qū)域银择。
棧
線程私有多糠,分為 Java 虛擬機棧和本地方法棧,存放局部變量表浩考、操作棧夹孔、動態(tài)鏈接、方法出口等信息析孽,方法的執(zhí)行對應(yīng)著入棧到出棧的過程搭伤。
方法區(qū)
線程共享,存放已被加載的類信息袜瞬、常量怜俐、靜態(tài)變量、即時編譯器編譯后的代碼等信息邓尤,JDK 1.8 中方法區(qū)被元空間取代拍鲤,使用直接內(nèi)存贴谎。
2、Java 類加載機制
image.png
加載
加載字節(jié)碼文件季稳。
鏈接
驗證
驗證字節(jié)碼文件的正確性擅这。
準(zhǔn)備
為靜態(tài)變量分配內(nèi)存。
解析
將符號引用(如類的全限定名)解析為直接引用(類在實際內(nèi)存中的地址)景鼠。
初始化
為靜態(tài)變量賦初值仲翎。
雙親委派模式
當(dāng)一個類需要加載時,判斷當(dāng)前類是否被加載過莲蜘。已經(jīng)被加載的類會直接返回谭确,否則才會嘗試加載。加載的時候票渠,首先會把該請求委派該父類加載器的loadClass()處理逐哈,因此所有的請求最終都應(yīng)該傳送到頂層的啟動類加載器BootstrapClassLoader中。當(dāng)父類加載器無法處理時问顷,才由自己來處理昂秃。當(dāng)父類加載器為 null 時,會使用啟動類加載器BootstrapClassLoader作為父類加載器杜窄。
3肠骆、垃圾回收算法
Mark-Sweep(標(biāo)記-清除)算法
標(biāo)記需要回收的對象,然后清除塞耕,會造成許多內(nèi)存碎片蚀腿。
Copying(復(fù)制)算法
將內(nèi)存分為兩塊,只使用一塊扫外,進行垃圾回收時莉钙,先將存活的對象復(fù)制到另一塊區(qū)域,然后清空之前的區(qū)域筛谚。
Mark-Compact(標(biāo)記-整理)算法(壓縮法)
與標(biāo)記清除算法類似磁玉,但是在標(biāo)記之后,將存活對象向一端移動驾讲,然后清除邊界外的垃圾對象蚊伞。
Generational Collection(分代收集)算法
分為年輕代和老年代,年輕代時比較活躍的對象吮铭,使用復(fù)制算法做垃圾回收时迫。老年代每次回收只回收少量對象,使用標(biāo)記整理法谓晌。
4悔捶、典型垃圾回收器
CMS
簡介
以獲取最短回收停頓時間為目標(biāo)的收集器谨读,它是一種并發(fā)收集器星持,采用的是 Mark-Sweep 算法。
場景
如果你的應(yīng)用需要更快的響應(yīng)烧董,不希望有長時間的停頓,同時你的 CPU 資源也比較豐富胧奔,就適合適用 CMS 收集器逊移。
垃圾回收步驟
初始標(biāo)記 (Stop the World 事件 CPU 停頓, 很短) 初始標(biāo)記僅標(biāo)記一下 GC Roots 能直接關(guān)聯(lián)到的對象龙填,速度很快胳泉;
并發(fā)標(biāo)記 (收集垃圾跟用戶線程一起執(zhí)行) 并發(fā)標(biāo)記過程就是進行 GC Roots 查找的過程;
重新標(biāo)記 (Stop the World 事件 CPU 停頓岩遗,比初始標(biāo)記稍微長扇商,遠比并發(fā)標(biāo)記短) 修正由于并發(fā)標(biāo)記時應(yīng)用運行產(chǎn)生變化的標(biāo)記。
并發(fā)清理宿礁,標(biāo)記清除算法案铺;
缺點
并發(fā)標(biāo)記時和應(yīng)用程序同時進行,占用一部分線程梆靖,所以吞吐量有所下降控汉。
并發(fā)清除時和應(yīng)用程序同時進行,這段時間產(chǎn)生的垃圾就要等下一次 GC 再清除返吻。
采用的標(biāo)記清除算法姑子,產(chǎn)生內(nèi)存碎片,如果要新建大對象测僵,會提前觸發(fā) Full GC 街佑。
G1
簡介
是一款面向服務(wù)端應(yīng)用的收集器,它能充分利用多 CPU捍靠、多核環(huán)境沐旨。因此它是一款并行與并發(fā)收集器,并且它能建立可預(yù)測的停頓時間模型剂公,即可以設(shè)置 STW 的時間希俩。
垃圾回收步驟
1吊宋、初始標(biāo)記(stop the world 事件 CPU 停頓只處理垃圾)纲辽;
2、并發(fā)標(biāo)記(與用戶線程并發(fā)執(zhí)行)璃搜;
3拖吼、最終標(biāo)記(stop the world 事件 ,CPU 停頓處理垃圾);
4这吻、篩選回收(stop the world 事件 根據(jù)用戶期望的 GC 停頓時間回收)
特點
并發(fā)與并行
充分利用多核 CPU 吊档,使用多核來縮短 STW 時間,部分需要停頓應(yīng)用線程的操作唾糯,仍然可以通過并發(fā)保證應(yīng)用程序的執(zhí)行怠硼。
分代回收
新生代鬼贱,幸存帶,老年代
空間整合
總體看是采用標(biāo)記整理算法回收香璃,每個 Region 大小相等这难,通過復(fù)制來回收。
可預(yù)測的停頓時間
使用 -XX:MaxGCPauseMillis=200 設(shè)置最長目標(biāo)暫停值葡秒。
在 Java 語言中姻乓,可作為 GC Roots 的對象包括 4 種情況:
a) 虛擬機棧中引用的對象(棧幀中的本地變量表);
b) 方法區(qū)中類靜態(tài)屬性引用的對象眯牧;
c) 方法區(qū)中常量引用的對象蹋岩;
d) 本地方法棧中 Native 方法引用的對象。
五学少、MySQL (Inno DB)
1剪个、聚簇索引與非聚簇索引
image.png
都使用 B+ 樹作為數(shù)據(jù)結(jié)構(gòu)
聚簇索引中數(shù)據(jù)存在主鍵索引的葉子結(jié)點中,得到 key 即得到 data 版确;非聚簇索引的數(shù)據(jù)存在單獨的空間禁偎。
聚簇索引中輔助索引的葉子結(jié)點存的是主鍵;非聚簇索引中葉子結(jié)點存的是數(shù)據(jù)的地址阀坏;
聚簇索引的優(yōu)勢是找到主鍵就找到數(shù)據(jù)如暖,只需一次磁盤 IO ;當(dāng) B+ 樹的結(jié)點發(fā)生變化時忌堂,地址也會發(fā)生變化盒至,這時非聚簇索引需要更新所有的地址,增加開銷士修。
2枷遂、為何使用 B 樹做索引而不是紅黑樹?
索引很大棋嘲,通常作為文件存儲在磁盤上面酒唉,每次檢索索引都需要把索引文件加載進內(nèi)存,所以磁盤 IO 的次數(shù)是衡量索引數(shù)據(jù)結(jié)構(gòu)好壞的重要指標(biāo)沸移。應(yīng)用程序在從磁盤讀取數(shù)據(jù)時痪伦,不只是讀取需要的數(shù)據(jù),還會連同其他數(shù)據(jù)以頁的形式做預(yù)讀來減少磁盤 IO 的次數(shù)雹锣。數(shù)據(jù)庫的設(shè)計者將每個節(jié)點的大小設(shè)置為一頁的大小网沾,同時每次新建節(jié)點時都重新申請一個頁,這樣檢索一個節(jié)點只需要一次 IO蕊爵,根據(jù)索引定位到數(shù)據(jù)只需要 h- 1(h 為 B 樹高度辉哥,根節(jié)點常駐內(nèi)存) 次 IO,而 d (度,可以理解為寬度)與 h 稱反比醋旦,即 d 越大恒水,高度就越小,所以樹越扁饲齐,磁盤 IO 次數(shù)越少寇窑,即漸進復(fù)雜度為 logdN ,這也是為什么不選擇紅黑樹做索引的原因箩张。前面可以得出結(jié)論甩骏,d 越大,索引的性能越好先慷。節(jié)點由 key 和 data 組成饮笛,頁的大小一定,key 和 data 越小论熙,d 越大福青。B + 樹去掉了節(jié)點內(nèi)的 data 域,所以有更大的 d , 性能更好脓诡。
3无午、最左前綴原則
在 MySQL 中,可以指定多個列為索引祝谚,即聯(lián)合索引宪迟。比如 index(name,age) 交惯,最左前綴原則是指查詢時精確匹配到從最左邊開始的一列或幾列(name次泽;name&age),就可以命中索引席爽。如果所有列都用到了意荤,順序不同,查詢引擎會自動優(yōu)化為匹配聯(lián)合索引的順序只锻,這樣是能夠命中索引的玖像。
4、什么情況下可以用到 B 樹索引
(1) 定義有主鍵的列一定要建立索引齐饮。因為主鍵可以加速定位到表中的某行
(2) 定義有外鍵的列一定要建立索引捐寥。外鍵列通常用于表與表之間的連接,在其上創(chuàng)建索引可以加快表間的連接
(3) 對于經(jīng)常查詢的數(shù)據(jù)列最好建立索引沈矿。
① 對于需要在指定范圍內(nèi)快速或頻繁查詢的數(shù)據(jù)列上真,因為索引已經(jīng)排序咬腋,其指定的范圍是連續(xù)的羹膳,查詢可以利用索引的排序,加快查詢的時間
② 經(jīng)常用在where子句中的數(shù)據(jù)列根竿,將索引建立在where子句的集合過程中陵像,對于需要加速或頻繁檢索的數(shù)據(jù)列就珠,可以讓這些經(jīng)常參與查詢的數(shù)據(jù)列按照索引的排序進行查詢,加快查詢的時間醒颖。
5妻怎、事務(wù)隔離級別
Read uncommitted
讀未提交,可能出現(xiàn)臟讀泞歉,不可重復(fù)讀逼侦,幻讀。
Read committed
讀提交腰耙,可能出現(xiàn)不可重復(fù)讀榛丢,幻讀。
Repeatable read
可重復(fù)讀挺庞,可能出現(xiàn)臟讀晰赞。
Serializable
可串行化,同一數(shù)據(jù)讀寫都加鎖选侨,避免臟讀掖鱼,性能不忍直視。
Inno DB 默認(rèn)隔離級別為可重復(fù)讀級別援制,分為快照度和當(dāng)前讀戏挡,并且通過行鎖和間隙鎖解決了幻讀問題。
6晨仑、MVCC (多版本并發(fā)控制)
實現(xiàn)細節(jié)
每行數(shù)據(jù)都存在一個版本增拥,每次數(shù)據(jù)更新時都更新該版本。
修改時 Copy 出當(dāng)前版本隨意修改寻歧,各個事務(wù)之間互不干擾掌栅。
保存時比較版本號,如果成功(commit)码泛,則覆蓋原記錄猾封;失敗則放棄 copy(rollback)。
Inno DB 實現(xiàn)
在 InnoDB 中為每行增加兩個隱藏的字段噪珊,分別是該行數(shù)據(jù)創(chuàng)建時的版本號和刪除時的版本號晌缘,這里的版本號是系統(tǒng)版本號(可以簡單理解為事務(wù)的 ID),每開始一個新的事務(wù)痢站,系統(tǒng)版本號就自動遞增磷箕,作為事務(wù)的 ID 。通常這兩個版本號分別叫做創(chuàng)建時間和刪除時間阵难。
詳細參考:《 臟讀岳枷、幻讀和不可重復(fù)讀》
六、Spring 相關(guān)
1、Bean 的作用域
|:---|:---|
| 類別 | 說明 |
|singleton| 默認(rèn)在 Spring 容器中僅存在一個實例 |
|prototype| 每次調(diào)用 getBean() 都重新生成一個實例 |
|request| 為每個 HTTP 請求生成一個實例 |
|session| 同一個 HTTP session 使用一個實例空繁,不同 session 使用不同實例 |
2殿衰、Bean 生命周期
簡單來說四步:
實例化 Instantiation
屬性賦值 Populate
初始化 Initialization
銷毀 Destruction
在這四步的基礎(chǔ)上面,Spring 提供了一些拓展點:
Bean 自身的方法: 這個包括了 Bean 本身調(diào)用的方法和通過配置文件中 %3Cbean %3E 的 init-method 和 destroy-method 指定的方法
Bean 級生命周期接口方法: 這個包括了 BeanNameAware盛泡、BeanFactoryAware闷祥、InitializingBean 和 DiposableBean 這些接口的方法
容器級生命周期接口方法:這個包括了 InstantiationAwareBeanPostProcessor 和 BeanPostProcessor 這兩個接口實現(xiàn),一般稱它們的實現(xiàn)類為“后處理器”傲诵。
工廠后處理器接口方法: 這個包括了 AspectJWeavingEnabler, ConfigurationClassPostProcessor, CustomAutowireConfigurer 等等非常有用的工廠后處理器接口的方法凯砍。工廠后處理器也是容器級的。在應(yīng)用上下文裝配配置文件之后立即調(diào)用拴竹。
3果覆、Spring AOP
實現(xiàn)方式兩種:
JDK 動態(tài)代理:帶有接口的對象,在運行期實現(xiàn)
CGlib 靜態(tài)代理:在編譯期實現(xiàn)殖熟。
4局待、Spring 事務(wù)傳播行為
默認(rèn)PROPAGATION_REQUIRED,如果存在一個事務(wù)菱属,則支持當(dāng)前事務(wù)钳榨。如果沒有事務(wù)則開啟一個新的事務(wù)。
image.png
5纽门、Spring IoC
image.png
6薛耻、Spring MVC 工作流程
七、計算機網(wǎng)絡(luò)
1赏陵、TCP/IP 五層模型
image.png
2饼齿、瀏覽器輸入地址后做了什么?
image.png
3蝙搔、三次握手與四次揮手
三次握手
image.png
四次揮手
image.png
4缕溉、TIME_WAIT 與 CLOSE_WAIT
image.png
5、TCP 滑動窗口
TCP 流量控制吃型,主要使用滑動窗口協(xié)議证鸥,滑動窗口是接受數(shù)據(jù)端使用的窗口大小,用來告訴發(fā)送端接收端的緩存大小勤晚,以此可以控制發(fā)送端發(fā)送數(shù)據(jù)的大小枉层,從而達到流量控制的目的。這個窗口大小就是我們一次傳輸幾個數(shù)據(jù)赐写。對所有數(shù)據(jù)幀按順序賦予編號鸟蜡,發(fā)送方在發(fā)送過程中始終保持著一個發(fā)送窗口,只有落在發(fā)送窗口內(nèi)的幀才允許被發(fā)送挺邀;同時接收方也維持著一個接收窗口揉忘,只有落在接收窗口內(nèi)的幀才允許接收跳座。
6、TCP 粘包和拆包
現(xiàn)象
image.png
產(chǎn)生原因
1癌淮、要發(fā)送的數(shù)據(jù)大于 TCP 發(fā)送緩沖區(qū)剩余空間大小躺坟,將會發(fā)生拆包沦补。
2乳蓄、待發(fā)送數(shù)據(jù)大于 MSS(最大報文長度),TCP 在傳輸前將進行拆包夕膀。
3虚倒、要發(fā)送的數(shù)據(jù)小于 TCP 發(fā)送緩沖區(qū)的大小,TCP 將多次寫入緩沖區(qū)的數(shù)據(jù)一次發(fā)送出去产舞,將會發(fā)生粘包魂奥。
4、接收數(shù)據(jù)端的應(yīng)用層沒有及時讀取接收緩沖區(qū)中的數(shù)據(jù)易猫,將發(fā)生粘包耻煤。
解決方式
1、發(fā)送端給每個數(shù)據(jù)包添加包首部准颓,首部中應(yīng)該至少包含數(shù)據(jù)包的長度哈蝇,這樣接收端在接收到數(shù)據(jù)后,通過讀取包首部的長度字段攘已,便知道每一個數(shù)據(jù)包的實際長度了炮赦。
2、發(fā)送端將每個數(shù)據(jù)包封裝為固定長度(不夠的可以通過補 0 填充)样勃,這樣接收端每次從接收緩沖區(qū)中讀取固定長度的數(shù)據(jù)就自然而然的把每個數(shù)據(jù)包拆分開來吠勘。
3、可以在數(shù)據(jù)包之間設(shè)置邊界峡眶,如添加特殊符號剧防,這樣,接收端通過這個邊界就可以將不同的數(shù)據(jù)包拆分開辫樱。
八诵姜、MQ 消息隊列
1、場景作用
削峰填谷搏熄,異步解耦棚唆。
2、如何保證消息不被重復(fù)消費呢心例?
這個問題可以換個思路宵凌,保證消息重復(fù)消費,其實是保證程序的冪等性止后。無論消息如何重復(fù)瞎惫,程序運行的結(jié)果是一致的溜腐。比如消費消息后做數(shù)據(jù)庫插入操作,為了防止消息重復(fù)消費瓜喇,可以在插入前先查詢一下有沒有對應(yīng)的數(shù)據(jù)挺益。
3、怎么保證從消息隊列里拿到的數(shù)據(jù)按順序執(zhí)行乘寒?
消費端在接收到消息后放入內(nèi)存隊列望众,然后對隊列中的消息進行有序消費。
4伞辛、如何解決消息隊列的延時以及過期失效問題烂翰?消息隊列滿了以后該怎么處理?有幾百萬消息持續(xù)積壓幾小時蚤氏,說說怎么解決甘耿?
消息過期失效問題,如果消息一段時間不消費竿滨,導(dǎo)致過期失效了佳恬,消息就丟失了,只能重新查出丟失的消息于游,重新發(fā)送毁葱。
再來說消息積壓的問題:(思路是快速消費掉積壓的消息)
首先排查消費端問題,恢復(fù)消費端正常消費速度曙砂。
然后著手處理隊列中的積壓消息头谜。
停掉現(xiàn)有的 consumer。
新建一個 topic 鸠澈,設(shè)置之前 10 倍的 partation柱告,之前 10 倍的隊列。
寫一個分發(fā)程序笑陈,將積壓的消息均勻的輪詢寫入這些隊列际度。
然后臨時用 10 倍的機器部署 consumer,每一批 consumer 消費 1 個臨時的隊列涵妥。
消費完畢后乖菱,恢復(fù)原有架構(gòu)。
消息隊列滿了:只能邊接收邊丟棄蓬网,然后重新補回丟失的消息窒所,再做消費。
4帆锋、如何保證消息的可靠性傳輸(如何處理消息丟失的問題)吵取?
kafka 為例:
消費者丟了數(shù)據(jù):
每次消息消費后,由自動提交 offset 改為手動提交 offset 锯厢。
kafka 丟了消息:
比較常見的一個場景皮官,就是 kafka 某個 broker 宕機脯倒,然后重新選舉 partition 的 leader 時。要是此時其他的 follower 剛好還有些數(shù)據(jù)沒有同步捺氢,結(jié)果此時 leader 掛了藻丢,然后大家選舉某個 follower 成為 leader 之后,不就少了一些數(shù)據(jù)摄乒。
給 topic 設(shè)置replication.factor參數(shù):這個值必須大于 1悠反,要求每個 partition 必須有至少兩個副本。
在 kafka 服務(wù)端設(shè)置min.insync.replicas參數(shù):這個值必須大于 1缺狠,這個是要求一個 leader 至少感知到有至少一個 follower 還跟自己保持聯(lián)系问慎,沒掉隊萍摊,這樣才能確保 leader 掛了還有一個 follower挤茄。
在 producer 端設(shè)置acks=all:這個是要求每條數(shù)據(jù),必須是寫入所有 replica 之后冰木,才能認(rèn)為是寫成功了穷劈。
在 producer 端設(shè)置retries=MAX(很大很大很大的一個值,無限次重試的意思):這個是要求一旦寫入失敗踊沸,就無限重試歇终,卡在這里。
生產(chǎn)者丟了消息:
如果按照上述的思路設(shè)置了 ack=all逼龟,一定不會丟评凝,要求是,你的 leader 接收到消息腺律,所有的 follower 都同步到了消息之后奕短,才認(rèn)為本次寫成功了。如果沒滿足這個條件匀钧,生產(chǎn)者會自動不斷的重試翎碑,重試無限次。
九之斯、Redis
1日杈、數(shù)據(jù)類型
String
常用命令: set,get,decr,incr,mget 等。
Hash
常用命令: hget,hset,hgetall 等
List
常用命令: lpush,rpush,lpop,rpop,lrange 等
可以通過 lrange 命令佑刷,就是從某個元素開始讀取多少個元素莉擒,可以基于 list 實現(xiàn)分頁查詢。
Set
常用命令: sadd,spop,smembers,sunion 等
Sort Set
常用命令: zadd,zrange,zrem,zcard 等
2瘫絮、Redis 如何實現(xiàn) key 的過期刪除涨冀?
定期刪除和惰性刪除的形式。
定期刪除
Redis 每隔一段時間從設(shè)置過期時間的 key 集合中檀何,隨機抽取一些 key 蝇裤,檢查是否過期廷支,如果已經(jīng)過期做刪除處理。
惰性刪除
Redis 在 key 被訪問的時候檢查 key 是否過期栓辜,如果過期則刪除恋拍。
3、Redis 的持久化機制
數(shù)據(jù)快照(RDB)+ 修改數(shù)據(jù)語句文件(AOF)
4藕甩、如何解決 Redis 緩存雪崩和緩存穿透施敢?
緩存雪崩
緩存同一時間大面積的失效,所以狭莱,后面的請求都會落到數(shù)據(jù)庫上僵娃,造成數(shù)據(jù)庫短時間內(nèi)承受大量請求而崩掉。
解決方式
事前:保證 Redis 集群的穩(wěn)定性腋妙,發(fā)現(xiàn)機器宕機盡快補上默怨,設(shè)置合適的內(nèi)存淘汰策略。
事中:本地緩存 + 限流降級骤素,避免大量請求落在數(shù)據(jù)庫上匙睹。
事后:利用 Redis 持久化機制盡快恢復(fù)緩存。
緩存穿透
一般是黑客故意去請求緩存中不存在的數(shù)據(jù)济竹,導(dǎo)致所有的請求都落到數(shù)據(jù)庫上痕檬,造成數(shù)據(jù)庫短時間內(nèi)承受大量請求而崩掉。
解決方式
將不存在的數(shù)據(jù)列舉到一個足夠大的 map 上送浊,這樣遭到攻擊時梦谜,直接攔截 map 中的請求,請求到數(shù)據(jù)庫上面袭景⊙渥或是把不存在的也做緩存,值為 null 浴讯,設(shè)置過期時間朵夏。
5、如何使用 Redis 實現(xiàn)消息隊列榆纽?
Redis 實現(xiàn)消息隊列依賴于 Redis 集群的穩(wěn)定性仰猖,通常不建議使用。
Redis 自帶發(fā)布訂閱功能奈籽,基于 publish 和 subscribe 命令饥侵。
使用 List 存儲消息,lpush衣屏,rpop 分別發(fā)送接收消息躏升。
十、Nginx
Nginx 是一款輕量級的 Web 服務(wù)器/反向代理服務(wù)器及電子郵件(IMAP/POP3)代理服務(wù)器狼忱。 Nginx 主要提供反向代
理膨疏、負(fù)載均衡一睁、動靜分離(靜態(tài)資源服務(wù))等服務(wù)。
1佃却、正向代理和反向代理
正向代理
代理客戶端訪問服務(wù)器者吁。典型:VPN
反向代理
代替服務(wù)器接收客戶端請求,然后轉(zhuǎn)發(fā)給服務(wù)器饲帅,服務(wù)器接收請求并將處理的結(jié)果通過代理服務(wù)器轉(zhuǎn)發(fā)給客戶端复凳。
2、負(fù)載均衡
將請求分?jǐn)偟蕉嗯_機器上去灶泵,高并發(fā)育八,增加吞吐量。
負(fù)載均衡算法
權(quán)重輪詢
fair
ip_hash
url_hash
3赦邻、動靜分離
動靜分離是讓動態(tài)網(wǎng)站里的動態(tài)網(wǎng)頁根據(jù)一定規(guī)則把不變的資源和經(jīng)常變的資源區(qū)分開來髓棋,動靜資源做好了拆分以后,我們就可以根據(jù)靜態(tài)資源的特點將其做緩存操作深纲,這就是網(wǎng)站靜態(tài)化處理的核心思路仲锄。
4劲妙、Nginx 四個組成部分
Nginx 二進制可執(zhí)行文件:由各模塊源碼編譯出一個文件
Nginx.conf 配置文件:控制 Nginx 行為
acess.log 訪問日志: 記錄每一條 HTTP 請求信息
error.log 錯誤日志:定位問題
作者:AYSAML
鏈接:http://www.reibang.com/p/2d46c351e30d
來源:簡書
著作權(quán)歸作者所有湃鹊。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處镣奋。