java多線程
1、線程池的原理削解,為什么要創(chuàng)建線程池营勤?
首先我們看下當(dāng)一個(gè)新的任務(wù)提交到線程池之后灵嫌,線程池是如何處理的
1、線程池判斷核心線程池里的線程是否都在執(zhí)行任務(wù)葛作。如果不是寿羞,則創(chuàng)建一個(gè)新的工作線程來執(zhí)行任務(wù)。如果核心線程池里的線程都在執(zhí)行任務(wù)赂蠢,則執(zhí)行第二步绪穆。
2、線程池判斷工作隊(duì)列是否已經(jīng)滿虱岂。如果工作隊(duì)列沒有滿玖院,則將新提交的任務(wù)存儲在這個(gè)工作隊(duì)列里進(jìn)行等待。如果工作隊(duì)列滿了第岖,則執(zhí)行第三步
3难菌、線程池判斷線程池的線程是否都處于工作狀態(tài)。如果沒有绍傲,則創(chuàng)建一個(gè)新的工作線程來執(zhí)行任務(wù)扔傅。如果已經(jīng)滿了,則交給飽和策略來處理這個(gè)任務(wù)
2烫饼、創(chuàng)建線程池的方式
newCachedThreadPool創(chuàng)建一個(gè)可緩存線程池,如果線程池長度超過處理需要试读,可靈活回收空閑線程杠纵,若無可回收,則新建線程钩骇。
newFixedThreadPool 創(chuàng)建一個(gè)定長線程池比藻,可控制線程最大并發(fā)數(shù),超出的線程會在隊(duì)列中等待倘屹。
newScheduledThreadPool 創(chuàng)建一個(gè)定長線程池银亲,支持定時(shí)及周期性任務(wù)執(zhí)行。
newSingleThreadExecutor 創(chuàng)建一個(gè)單線程化的線程池纽匙,它只會用唯一的工作線程來執(zhí)行任務(wù)务蝠,保證所有任務(wù)按照指定順序(FIFO, LIFO, 優(yōu)先級)執(zhí)行。
3烛缔、線程的生命周期
新建狀態(tài)(New)
就緒狀態(tài)(Runnable)
運(yùn)行狀態(tài)(Running)
阻塞狀態(tài)(Blocked)
死亡狀態(tài)
4馏段、如何實(shí)現(xiàn)線程安全
synchronized關(guān)鍵字等(CAS)
5轩拨、 volatile和synchronized的區(qū)別
volatile本質(zhì)是在告訴jvm當(dāng)前變量在寄存器(工作內(nèi)存)中的值是不確定的,需要從主存中讀仍合病亡蓉; synchronized則是鎖定當(dāng)前變量,只有當(dāng)前線程可以訪問該變量喷舀,其他線程被阻塞住砍濒。
volatile僅能使用在變量級別;synchronized則可以使用在變量硫麻、方法爸邢、和類級別的
volatile僅能實(shí)現(xiàn)變量的修改可見性,不能保證原子性庶香;而synchronized則可以保證變量的修改可見性和原子性
volatile不會造成線程的阻塞甲棍;synchronized可能會造成線程的阻塞。
volatile標(biāo)記的變量不會被編譯器優(yōu)化赶掖;synchronized標(biāo)記的變量可以被編譯器優(yōu)化
JVM相關(guān)
1感猛、JVM內(nèi)存模型,GC機(jī)制和原理
程序計(jì)數(shù)器奢赂、本地方法棧陪白、方法區(qū)、Java棧膳灶、Java堆及其他隱含寄存器
垃圾收集器一般必須完成兩件事:檢測出垃圾咱士;回收垃圾。怎么檢測出垃圾轧钓?一般有以下幾種方法:引用計(jì)數(shù)法序厉、可達(dá)性分析算法等
2、GC分哪兩種毕箍,Minor GC 和Full GC有什么區(qū)別弛房?什么時(shí)候會觸發(fā)Full GC?
GC的種類:普通GC(minor GC):只針對新生代區(qū)域的GC而柑、全局GC(major GC or Full GC):針對年老代的GC文捶,偶爾伴隨對新生代的GC以及對永久代的GC。
Full GC的觸發(fā)條件:
1媒咳、System.gc()方法的調(diào)用
2粹排、老年代代空間不足
3、永生區(qū)空間不足
4涩澡、CMS GC時(shí)出現(xiàn)promotion failed和concurrent mode failure
5顽耳、統(tǒng)計(jì)得到的Minor GC晉升到舊生代的平均大小大于老年代的剩余空間
6、堆中分配很大的對象
3、JVM里的有幾種classloader
Bootstrp loader
Bootstrp加載器是用C++語言寫的斧抱,它是在Java虛擬機(jī)啟動后初始化的常拓,它主要負(fù)責(zé)加載%JAVA_HOME%/jre/lib,-Xbootclasspath參數(shù)指定的路徑以及%JAVA_HOME%/jre/classes中的類。
ExtClassLoader
Bootstrp loader加載ExtClassLoader,并且將ExtClassLoader的父加載器設(shè)置為Bootstrp loader.ExtClassLoader是用Java寫的辉浦,具體來說就是 sun.misc.Launcher$ExtClassLoader弄抬,ExtClassLoader主要加載%JAVA_HOME%/jre/lib/ext,此路徑下的所有classes目錄以及java.ext.dirs系統(tǒng)變量指定的路徑中類庫宪郊。
AppClassLoader
Bootstrp loader加載完ExtClassLoader后掂恕,就會加載AppClassLoader,并且將AppClassLoader的父加載器指定為 ExtClassLoader。AppClassLoader也是用Java寫成的弛槐,它的實(shí)現(xiàn)類是 sun.misc.Launcher$AppClassLoader懊亡,另外我們知道ClassLoader中有個(gè)getSystemClassLoader方法,此方法返回的正是AppclassLoader.AppClassLoader主要負(fù)責(zé)加載classpath所指定的位置的類或者是jar文檔,它也是Java程序默認(rèn)的類加載器乎串。
4店枣、什么是雙親委派機(jī)制?介紹一些運(yùn)作過程叹誉,雙親委派模型的好處
如果一個(gè)類加載器收到了類加載的請求鸯两,它首先不會自己去嘗試加載這個(gè)類,而是把這個(gè)請求委派給父類加載器去完成长豁。
每一個(gè)層次的類加載器都是如此钧唐。因此,所有的加載請求最終都應(yīng)該傳送到頂層的啟動類加載器中匠襟。
只有當(dāng)父加載器反饋?zhàn)约簾o法完成這個(gè)加載請求時(shí)(搜索范圍中沒有找到所需的類)钝侠,子加載器才會嘗試自己去加載。
采用雙親委派模式的是好處是Java類隨著它的類加載器一起具備了一種帶有優(yōu)先級的層次關(guān)系酸舍,通過這種層級關(guān)可以避免類的重復(fù)加載帅韧,當(dāng)父親已經(jīng)加載了該類時(shí),就沒有必要子ClassLoader再加載一次啃勉。其次是考慮到安全因素弱匪,java核心api中定義類型不會被隨意替換,假設(shè)通過網(wǎng)絡(luò)傳遞一個(gè)名為java.lang.Integer的類璧亮,通過雙親委托模式傳遞到啟動類加載器,而啟動類加載器在核心Java API發(fā)現(xiàn)這個(gè)名字的類斥难,發(fā)現(xiàn)該類已被加載枝嘶,并不會重新加載網(wǎng)絡(luò)傳遞的過來的java.lang.Integer,而直接返回已加載過的Integer.class哑诊,這樣便可以防止核心API庫被隨意篡改群扶。
5、什么情況下我們需要破壞雙親委派模型
雙親委派模型的第二次“被破壞”是由這個(gè)模型自身的缺陷所導(dǎo)致的,雙親委派很好地解決了各個(gè)類加載器的基礎(chǔ)類的統(tǒng)一問題(越基礎(chǔ)的類由越上層的加載器進(jìn)行加載)竞阐,基礎(chǔ)類之所以稱為“基礎(chǔ)”缴饭,是因?yàn)樗鼈兛偸亲鳛楸挥脩舸a調(diào)用的API,但世事往往沒有絕對的完美骆莹,如果基礎(chǔ)類又要調(diào)用回用戶的代碼颗搂,那該怎么辦?
這并非是不可能的事情幕垦,一個(gè)典型的例子便是JNDI服務(wù)丢氢,JNDI現(xiàn)在已經(jīng)是Java的標(biāo)準(zhǔn)服務(wù),它的代碼由啟動類加載器去加載(在JDK 1.3時(shí)放進(jìn)去的rt.jar)先改,但JNDI的目的就是對資源進(jìn)行集中管理和查找疚察,它需要調(diào)用由獨(dú)立廠商實(shí)現(xiàn)并部署在應(yīng)用程序的ClassPath下的JNDI接口提供者(SPI,Service Provider Interface)的代碼,但啟動類加載器不可能“認(rèn)識”這些代碼俺鹉獭貌嫡!那該怎么辦?
為了解決這個(gè)問題该溯,Java設(shè)計(jì)團(tuán)隊(duì)只好引入了一個(gè)不太優(yōu)雅的設(shè)計(jì):線程上下文類加載器(Thread Context ClassLoader)岛抄。這個(gè)類加載器可以通過java.lang.Thread類的setContextClassLoaser()方法進(jìn)行設(shè)置,如果創(chuàng)建線程時(shí)還未設(shè)置朗伶,它將會從父線程中繼承一個(gè)弦撩,如果在應(yīng)用程序的全局范圍內(nèi)都沒有設(shè)置過的話,那這個(gè)類加載器默認(rèn)就是應(yīng)用程序類加載器论皆。
有了線程上下文類加載器益楼,就可以做一些“舞弊”的事情了,JNDI服務(wù)使用這個(gè)線程上下文類加載器去加載所需要的SPI代碼点晴,也就是父類加載器請求子類加載器去完成類加載的動作感凤,這種行為實(shí)際上就是打通了雙親委派模型的層次結(jié)構(gòu)來逆向使用類加載器,實(shí)際上已經(jīng)違背了雙親委派模型的一般性原則粒督,但這也是無可奈何的事情陪竿。Java中所有涉及SPI的加載動作基本上都采用這種方式,例如JNDI屠橄、JDBC族跛、JCE、JAXB和JBI等锐墙。
6礁哄、常見的JVM調(diào)優(yōu)方法有哪些?可以具體到調(diào)整哪個(gè)參數(shù)溪北,調(diào)成什么值桐绒?
-Xms:初始堆大小
-Xms:最大堆大小
-XX:NewSize=n:設(shè)置年輕代大小
-XX:NewRatio=n:設(shè)置年輕代和年老代的比值夺脾。如:為3表示年輕代和年老代比值為1:3,年輕代占整個(gè)年輕代年老代和的1/4
-XX:SurvivorRatio=n:年輕代中Eden區(qū)與兩個(gè)Survivor區(qū)的比值茉继。注意Survivor區(qū)有兩個(gè)咧叭。如3表示Eden: 3 Survivor:2,一個(gè)Survivor區(qū)占整個(gè)年輕代的1/5
-XX:MaxPermSize=n:設(shè)置持久代大小
java高級部分
1烁竭、紅黑樹的實(shí)現(xiàn)原理和應(yīng)用場景
紅黑樹是一種 自平衡 的二叉樹菲茬,所謂的自平衡是指在插入和刪除的過程中,紅黑樹會采取一定的策略對樹的組織形式進(jìn)行調(diào)整颖变,以盡可能的減少樹的高度生均,從而節(jié)省查找的時(shí)間。紅黑樹的特性如下:
結(jié)點(diǎn)是紅色或黑色
根結(jié)點(diǎn)始終是黑色
葉子結(jié)點(diǎn)(NIL 結(jié)點(diǎn))都是黑色
紅色結(jié)點(diǎn)的兩個(gè)直接孩子結(jié)點(diǎn)都是黑色(即從葉子到根的所有路徑上不存在兩個(gè)連續(xù)的紅色結(jié)點(diǎn))
從任一結(jié)點(diǎn)到每個(gè)葉子的所有簡單路徑都包含相同數(shù)目的黑色結(jié)點(diǎn)
(其他自己去了解)
2腥刹、NIO是什么马胧?適用于何種場景?
1衔峰、IO是面向流的佩脊,NIO是面向塊(緩沖區(qū))的。
IO面向流的操作一次一個(gè)字節(jié)地處理數(shù)據(jù)垫卤。一個(gè)輸入流產(chǎn)生一個(gè)字節(jié)的數(shù)據(jù)威彰,一個(gè)輸出流消費(fèi)一個(gè)字節(jié)的數(shù)據(jù)。穴肘,導(dǎo)致了數(shù)據(jù)的讀取和寫入效率不佳歇盼;
NIO面向塊的操作在一步中產(chǎn)生或者消費(fèi)一個(gè)數(shù)據(jù)塊。按塊處理數(shù)據(jù)比按(流式的)字節(jié)處理數(shù)據(jù)要快得多评抚,同時(shí)數(shù)據(jù)讀取到一個(gè)它稍后處理的緩沖區(qū)豹缀,需要時(shí)可在緩沖區(qū)中前后移動。這就增加了處理過程中的靈活性慨代。通俗來說邢笙,NIO采取了“預(yù)讀”的方式,當(dāng)你讀取某一部分?jǐn)?shù)據(jù)時(shí)侍匙,他就會猜測你下一步可能會讀取的數(shù)據(jù)而預(yù)先緩沖下來氮惯。
2、IO是阻塞的想暗,NIO是非阻塞的妇汗。
對于傳統(tǒng)的IO,當(dāng)一個(gè)線程調(diào)用read() 或 write()時(shí)说莫,該線程被阻塞铛纬,直到有一些數(shù)據(jù)被讀取,或數(shù)據(jù)完全寫入唬滑。該線程在此期間不能再干任何事情了。
而對于NIO,使用一個(gè)線程發(fā)送讀取數(shù)據(jù)請求晶密,沒有得到響應(yīng)之前擒悬,線程是空閑的,此時(shí)線程可以去執(zhí)行別的任務(wù)稻艰,而不是像IO中那樣只能等待響應(yīng)完成懂牧。
NIO和IO適用場景
NIO是為彌補(bǔ)傳統(tǒng)IO的不足而誕生的,但是尺有所短寸有所長尊勿,NIO也有缺點(diǎn)僧凤,因?yàn)镹IO是面向緩沖區(qū)的操作,每一次的數(shù)據(jù)處理都是對緩沖區(qū)進(jìn)行的元扔,那么就會有一個(gè)問題躯保,在數(shù)據(jù)處理之前必須要判斷緩沖區(qū)的數(shù)據(jù)是否完整或者已經(jīng)讀取完畢,如果沒有澎语,假設(shè)數(shù)據(jù)只讀取了一部分途事,那么對不完整的數(shù)據(jù)處理沒有任何意義。所以每次數(shù)據(jù)處理之前都要檢測緩沖區(qū)數(shù)據(jù)擅羞。
3尸变、HashMap內(nèi)部的數(shù)據(jù)結(jié)構(gòu)是什么?底層是怎么實(shí)現(xiàn)的减俏?
HashMap由數(shù)組+鏈表組成的召烂,數(shù)組是HashMap的主體,鏈表則是主要為了解決哈希沖突而存在的娃承,如果定位到的數(shù)組位置不含鏈表(當(dāng)前entry的next指向null),那么對于查找奏夫,添加等操作很快,僅需一次尋址即可草慧;如果定位到的數(shù)組包含鏈表桶蛔,對于添加操作,其時(shí)間復(fù)雜度依然為O(1)漫谷,因?yàn)樽钚碌腅ntry會插入鏈表頭部仔雷,僅需簡單改變引用鏈即可,而對于查找操作來講舔示,此時(shí)就需要遍歷鏈表碟婆,然后通過key對象的equals方法逐一比對查找。所以惕稻,性能考慮竖共,HashMap中的鏈表出現(xiàn)越少,性能才會越好俺祠。
4公给、List 和 Map 區(qū)別借帘,Arraylist 與 LinkedList 區(qū)別,ArrayList 與 Vector 區(qū)別
Vector淌铐、ArrayList肺然、LinkedList這三者都實(shí)現(xiàn)了List 接口.所有使用方式也很相似,主要區(qū)別在于實(shí)現(xiàn)方式的不同,所以對不同的操作具有不同的效率。
ArrayList 就是動態(tài)數(shù)組腿准,是Array的復(fù)雜版本际起,動態(tài)的增加和減少元素.當(dāng)更多的元素加入到ArrayList中時(shí),其大小將會動態(tài)地增長。它的元素可以通過get/set方法直接訪問吐葱,因?yàn)锳rrayList本質(zhì)上是一個(gè)數(shù)組街望。
Vector 和ArrayList類似, 區(qū)別在于Vector是同步類(synchronized).因此,開銷就比ArrayList要大。
LinkedList 是一個(gè)雙鏈表,在添加和刪除元素時(shí)具有比ArrayList更好的性能.但在get與set方面弱于ArrayList.當(dāng)然,這些對比都是指數(shù)據(jù)量很大或者操作很頻繁的情況下的對比弟跑。它還實(shí)現(xiàn)了 Queue 接口,該接口比List提供了更多的方法,包括 offer(),peek(),poll()等.
Spring相關(guān)
1灾前、Spring AOP的實(shí)現(xiàn)原理和場景?
Spring AOP使用的動態(tài)代理窖认,所謂的動態(tài)代理就是說AOP框架不會去修改字節(jié)碼豫柬,而是在內(nèi)存中臨時(shí)為方法生成一個(gè)AOP對象,這個(gè)AOP對象包含了目標(biāo)對象的全部方法扑浸,并且在特定的切點(diǎn)做了增強(qiáng)處理烧给,并回調(diào)原對象的方法。
Spring AOP中的動態(tài)代理主要有兩種方式喝噪,JDK動態(tài)代理和CGLIB動態(tài)代理础嫡。JDK動態(tài)代理通過反射來接收被代理的類,并且要求被代理的類必須實(shí)現(xiàn)一個(gè)接口酝惧。JDK動態(tài)代理的核心是InvocationHandler接口和Proxy類榴鼎。
如果目標(biāo)類沒有實(shí)現(xiàn)接口,那么Spring AOP會選擇使用CGLIB來動態(tài)代理目標(biāo)類晚唇。CGLIB(Code Generation Library)巫财,是一個(gè)代碼生成的類庫,可以在運(yùn)行時(shí)動態(tài)的生成某個(gè)類的子類哩陕,注意平项,CGLIB是通過繼承的方式做的動態(tài)代理,因此如果某個(gè)類被標(biāo)記為final悍及,那么它是無法使用CGLIB做動態(tài)代理的闽瓢。
2、Spring IOC是什么心赶?優(yōu)點(diǎn)是什么扣讼?
若要理解Spring IoC的優(yōu)點(diǎn),首先要理解控制反轉(zhuǎn)的思想缨叫⊥址控制反轉(zhuǎn)(Inversion of Control荔燎,縮寫為IoC),是面向?qū)ο缶幊讨械囊环N設(shè)計(jì)原則艰山,可以用來減低計(jì)算機(jī)代碼之間的耦合度湖雹。其中最常見的方式叫做依賴注入(Dependency Injection,簡稱DI)曙搬,還有一種方式叫“依賴查找”(Dependency Lookup)。通過控制反轉(zhuǎn)鸽嫂,對象在被創(chuàng)建的時(shí)候纵装,由一個(gè)調(diào)控系統(tǒng)(IOC容器),將其所依賴的對象的引用傳遞給它据某。也可以說橡娄,依賴被注入到對象中⊙⒆眩控制是否被反轉(zhuǎn)其實(shí)正是框架和庫(Framework and Library)的區(qū)別挽唉。客戶程序員使用庫筷狼,框架使用客戶程序員瓶籽。對于一個(gè)庫而言,用戶程序員使用的方式是主動調(diào)用它埂材,這是通常情況的做法塑顺,也就是“正向”控制;而對于一個(gè)框架俏险,往往將用戶程序員編寫的代碼注冊到框架中严拒,最后由框架來調(diào)用用戶程序員編寫的代碼,這就構(gòu)成了控制反轉(zhuǎn)竖独。也就是說裤唠,控制反轉(zhuǎn)的關(guān)鍵在于“控制者”是誰。對于一個(gè)庫而言莹痢,復(fù)用的可能只是算法和數(shù)據(jù)結(jié)構(gòu)种蘸;而對于一個(gè)框架而言,復(fù)用的往往還有控制流邏輯格二,這也是控制反轉(zhuǎn)的結(jié)果劈彪。
數(shù)據(jù)庫篇
1、Redis集群架構(gòu)有哪些顶猜、常用的持久化方式是什么沧奴。以及其原理
a、Replication+Sentinel
b长窄、Proxy+Replication+Sentinel
c滔吠、Redis Cluster(主要)
RDB持久化機(jī)制對redis中的數(shù)據(jù)執(zhí)行周期性的持久化纲菌。
AOF持久化機(jī)制對每條寫入命令作為日志,以append-only模式寫入一個(gè)日志文件中疮绷,在redis重啟的時(shí)候翰舌,可以通過AOF寫入的指令來重新構(gòu)建整個(gè)數(shù)據(jù)集。
安全篇
1冬骚、Https的流程:
a椅贱、瀏覽器發(fā)出安全請求
b、服務(wù)器發(fā)送數(shù)字證書只冻,包含服務(wù)器的public key
c庇麦、瀏覽器用預(yù)置的CA列表驗(yàn)證證書,如果有問題喜德,則提示風(fēng)險(xiǎn)
d山橄、瀏覽器生成隨機(jī)的對稱秘鑰,用服務(wù)器的public key加密
e舍悯、服務(wù)器用自己的pricate key進(jìn)行解密航棱,得到對稱秘鑰
f、雙方都知道了對稱秘鑰萌衬,用它來加密通信
線上問題定位
1饮醇、談?wù)劸€上CPU100%排查套路
1、查消耗cpu最高的進(jìn)程Pid
2奄薇、根據(jù)Pid查出消耗cpu最高的線程號
3驳阎、根據(jù)線程號查出對應(yīng)的java線程,進(jìn)行處理馁蒂。(jstack -l 3033 > ./3033.stack)