Java面試大綱

答案不全,歡迎補(bǔ)充訂正~

Java基礎(chǔ)

1、List 和 Set 的區(qū)別~List允許重復(fù),有序容器,按照存放順序存放,允許多個(gè)null值。

~Set不允許重復(fù),無序容器,只允許有一個(gè)null值。

2、HashSet 是如何保證不重復(fù)的~HashSet的構(gòu)造方法就是在內(nèi)部實(shí)例化了一個(gè)HashMap對(duì)象,在存儲(chǔ)數(shù)據(jù)時(shí)將值作為map的key,value則為一個(gè)finale修飾的變量,因?yàn)镠ashMap不允許有重復(fù)的key,所以實(shí)現(xiàn)了HashSet無重復(fù)值。

3、HashMap 是線程安全的嗎,為什么不是線程安全的(最好畫圖說明多線程環(huán)境下不安全)?~線程不安全,put方法和resize 都是非同步方法。

4、HashMap 的擴(kuò)容過程~在擴(kuò)容過程中,會(huì)新生成一個(gè)新的容量的數(shù)組,然后對(duì)原數(shù)組的所有鍵值對(duì)重新進(jìn)行計(jì)算和寫入新的數(shù)組,之后指向新生成的數(shù)組。

5、HashMap 1.7 與 1.8 的 區(qū)別,說明 1.8 做了哪些優(yōu)化,如何優(yōu)化的?~ JDK1.8引入紅黑樹大程度優(yōu)化了HashMap的性能。當(dāng)鏈表的長度大于8時(shí),轉(zhuǎn)換為紅黑樹的結(jié)構(gòu)。

6、final finally finalize~1-final是一個(gè)修飾符,可以修飾變量,方法和類,如果final修飾變量,意味著該變量的值在初始化之后就不能被改變。

2-finally是一個(gè)關(guān)鍵字,與try和catch一起用于異常的處理,finally塊一定會(huì)被執(zhí)行,無論在try塊中是否發(fā)生異常。

3-finallize方法是在對(duì)象被回收之前調(diào)用的方法,給對(duì)象自己最后一個(gè)復(fù)活的機(jī)會(huì),但是什么時(shí)候調(diào)用finallize沒有保證。

7、強(qiáng)引用 、軟引用、 弱引用、虛引用~1-強(qiáng)引用:如果一個(gè)對(duì)象具有強(qiáng)引用,它就不會(huì)被垃圾回收器回收,即使當(dāng)前內(nèi)存空間不足,JVM也不會(huì)回收它,而是拋出OutOfMemoryError,使程序異常終止;2-軟引用:在使用軟引用時(shí),如果內(nèi)存的空間足夠,軟應(yīng)用就能繼續(xù)被使用,而不會(huì)被回收,只有在內(nèi)存不足的時(shí)候才會(huì)被回收;3-弱引用:具有弱引用的對(duì)象擁有的生命周期更短暫,因?yàn)楫?dāng)JVM一旦發(fā)現(xiàn)弱引用對(duì)象,無論當(dāng)前內(nèi)存空間是否充足,都會(huì)將弱引用回收;

4-虛引用:如果一個(gè)對(duì)象僅持有虛引用,那么它相當(dāng)于沒有引用,在任何時(shí)候都有可能被垃圾回收。

8、Java反射

~Reflection(反射)是Java被視為動(dòng)態(tài)語言的關(guān)鍵,反射機(jī)制允許程序在執(zhí)行期借助于Reflection API取得任何類的內(nèi)部信息,并能直接操作任意對(duì)象的內(nèi)部屬性及方法。Java反射機(jī)制主要提供了以下功能:1-在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象;2-在運(yùn)行時(shí)獲取任意一個(gè)類所具有的成員變量和方法;3-在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的方法(屬性);4-生成動(dòng)態(tài)代理;Java 并發(fā)

1、synchronized 的實(shí)現(xiàn)原理以及鎖優(yōu)化?~Java虛擬機(jī)中的同步(Synchronization)都是基于進(jìn)入和退出Monitor對(duì)象實(shí)現(xiàn),無論是顯示同步(同步代碼塊)還是隱式同步(同步方法)都是如此。

JDK1.6之后JVM官方對(duì)鎖做了較大優(yōu)化:

引入了:

鎖粗化(Lock Coarsening)

鎖消除(Lock Elimination)

適應(yīng)性自旋(Adaptive Spinning)

同時(shí)增加了兩種鎖的狀態(tài):

偏向鎖(Biased Locking)

輕量鎖(Lightweight Locking)

詳細(xì):

參考收藏筆記

2、volatile 的實(shí)現(xiàn)原理?

~1.將當(dāng)前處理器緩存行的數(shù)據(jù)會(huì)寫回到系統(tǒng)內(nèi)存。

? 2.這個(gè)寫回內(nèi)存的操作會(huì)引起在其他CPU里緩存了該內(nèi)存地址的數(shù)據(jù)無效。

3、Java 的信號(hào)燈?

~Semaphore是Java1.5之后提供的一種同步工具,Semaphore可以維護(hù)訪問自身線程個(gè)數(shù),并提供了同步機(jī)制。使用Semaphore可以控制同時(shí)訪問資源的線程個(gè)數(shù),通過?acquire()?獲取一個(gè)許可,如果沒有就等待,而release()?釋放一個(gè)許可。

4、synchronized 在靜態(tài)方法和普通方法的區(qū)別?

~synchronized修飾不加static的方法,鎖是加在單個(gè)對(duì)象上,不同的對(duì)象沒有競爭關(guān)系;修飾加了static的方法,鎖是加載類上,這個(gè)類所有的對(duì)象競爭一把鎖。5、怎么實(shí)現(xiàn)所有線程在等待某個(gè)事件的發(fā)生才會(huì)去執(zhí)行?~java里面實(shí)現(xiàn)這個(gè)有兩個(gè)辦法,countdownlatch和cyclicbarrier。

cyclicbarrier可以重復(fù)使用,它允許一組線程相互等待,直到達(dá)到某個(gè)公共屏障點(diǎn)。cyclicbarrier不會(huì)阻塞主線程,只會(huì)阻塞子線程。

countdownlatch不可以重復(fù)使用,會(huì)阻塞主線程。主線程調(diào)用await方法,主線程阻塞。子線程調(diào)用countdown方法,觸發(fā)計(jì)數(shù)。countdownlatch內(nèi)部是實(shí)現(xiàn)了AQS,初始化的時(shí)候,new? CountDownLatch(n);將AQS的state設(shè)置為n。

6、CAS?CAS 有什么缺陷,如何解決?~CAS(Compare-and-Swap),即比較并替換,是一種實(shí)現(xiàn)并發(fā)算法時(shí)常用到的技術(shù)。

缺陷:

1-循環(huán)時(shí)間長開銷很大。

getAndAddInt方法執(zhí)行時(shí),只有完成覆蓋才會(huì)調(diào)出循環(huán),如果CAS失敗,會(huì)一直進(jìn)行嘗試。如果CAS長時(shí)間一直不成功,可能會(huì)給CPU帶來很大的開銷。

2-只能保證一個(gè)共享變量的原子操作。

當(dāng)對(duì)一個(gè)共享變量執(zhí)行操作時(shí),我們可以使用循環(huán)CAS的方式來保證原子操作,但是對(duì)多個(gè)共享變量操作時(shí),循環(huán)CAS就無法保證操作的原子性,這個(gè)時(shí)候就可以用鎖來保證原子性。

3-ABA問題。

如果內(nèi)存地址V初次讀取的值是A,并且在準(zhǔn)備賦值的時(shí)候檢查到它的值仍然為A,如果在這段期間它的值曾經(jīng)被改成了B,后來又被改回為A,那CAS操作就會(huì)誤認(rèn)為它從來沒有被改變過。這個(gè)漏洞稱為CAS操作的“ABA”問題,Java并發(fā)包為了解決這個(gè)問題,提供了一個(gè)帶有標(biāo)記的原子引用類“AtomicStampedReference”,它可以通過控制變量值的版本來保證CAS的正確性。

7、synchronized 和 lock 有什么區(qū)別?~1-Lock不是Java語言內(nèi)置的,synchronized是Java語言的關(guān)鍵字,因此是內(nèi)置特性。Lock是一個(gè)類,通過這個(gè)類可以實(shí)現(xiàn)同步訪問;

2-Lock和synchronized有一點(diǎn)非常大的不同,采用synchronized不需要用戶去手動(dòng)釋放鎖,當(dāng)synchronized方法或者synchronized代碼塊執(zhí)行完之后,系統(tǒng)會(huì)自動(dòng)讓線程釋放對(duì)鎖的占用;而Lock則必須要用戶去手動(dòng)釋放鎖,如果沒有主動(dòng)釋放鎖,就有可能導(dǎo)致出現(xiàn)死鎖現(xiàn)象。

8、Hashtable 是怎么加鎖的 ?~ Hashtable ht = Hashtable.Synchronized(new Hashtable());

9、HashMap 的并發(fā)問題?~1-多線程put后可能導(dǎo)致get死循環(huán)

2-多線程put的時(shí)候可能導(dǎo)致元素丟失

3-put非null元素后get出來的卻是null

會(huì)造成的嚴(yán)重的cpu飆升問題,從而產(chǎn)生內(nèi)存泄露。

10、ConcurrentHashMap 介紹?1.8 中為什么要用紅黑樹?~ConcurrentHashMap是在JDK 1.5上添加的并發(fā)收集類,用于替代基于哈希的同步映射實(shí)現(xiàn),例如Hashtable和HashMap。他們的同步對(duì)手提供更好的性能和可擴(kuò)展性,風(fēng)險(xiǎn)很小。

因?yàn)榧t黑樹的性能特點(diǎn),在大量 hashcode 值相同的時(shí)候,查找某個(gè)特定元素,也只是需要O(log n) 的開銷。

11、AQS~AQS是AbustactQueuedSynchronizer的簡稱,它是一個(gè)Java提供的底層同步工具類,用一個(gè)int類型的變量表示同步狀態(tài),并提供了一系列的CAS操作來管理這個(gè)同步狀態(tài)。AQS的主要作用是為Java中的并發(fā)同步組件提供統(tǒng)一的底層支持,例如ReentrantLock(可重入鎖),CountdownLatch(閉鎖)就是基于AQS實(shí)現(xiàn)的,用法是通過繼承AQS實(shí)現(xiàn)其模版方法,然后將子類作為同步組件的內(nèi)部類。

12、如何檢測死鎖?怎么預(yù)防死鎖?~可以使用 jstack或者pstack 和 gdb 工具對(duì)死鎖程序進(jìn)行分析。

預(yù)防死鎖的方法是通過設(shè)置某些限制條件,去破壞產(chǎn)生死鎖的四個(gè)必要條件中的一個(gè)或者幾個(gè),以避免發(fā)生死鎖。

由于互斥條件是非共享設(shè)備所必須的,不僅不能改變,還應(yīng)該加以保證,因此,主要是破壞產(chǎn)生死鎖的后三個(gè)條件。

1-破壞請(qǐng)求與保持條件:對(duì)一個(gè)進(jìn)程在請(qǐng)求資源時(shí),它不能持有不可剝奪資源。

2-破壞不可剝奪條件:對(duì)一個(gè)已經(jīng)保持了某些不可剝奪資源的進(jìn)程,提出新的資源請(qǐng)求而不能得到滿足時(shí),它必須釋放已經(jīng)保持的所有資源,待以后需要時(shí)再重新申請(qǐng)。

3-破壞循環(huán)等待條件:對(duì)系統(tǒng)所有資源類型進(jìn)行線性排列,并賦予不同序號(hào)。規(guī)定每個(gè)進(jìn)程必須按序號(hào)遞增的順序請(qǐng)求資源。

13、Java 內(nèi)存模型?~Java內(nèi)存模型(即Java Memory Model,簡稱JMM)本身是一種抽象的概念,并不真實(shí)存在,它描述的是一組規(guī)則或規(guī)范,通過這組規(guī)范定義了程序中各個(gè)變量(包括實(shí)例字段,靜態(tài)字段和構(gòu)成數(shù)組對(duì)象的元素)的訪問方式。由于JVM運(yùn)行程序的實(shí)體是線程,而每個(gè)線程創(chuàng)建時(shí)JVM都會(huì)為其創(chuàng)建一個(gè)工作內(nèi)存(有些地方稱為??臻g),用于存儲(chǔ)線程私有的數(shù)據(jù),而Java內(nèi)存模型中規(guī)定所有變量都存儲(chǔ)在主內(nèi)存,主內(nèi)存是共享內(nèi)存區(qū)域,所有線程都可以訪問,但線程對(duì)變量的操作(讀取賦值等)必須在工作內(nèi)存中進(jìn)行,首先要將變量從主內(nèi)存拷貝的自己的工作內(nèi)存空間,然后對(duì)變量進(jìn)行操作,操作完成后再將變量寫回主內(nèi)存,不能直接操作主內(nèi)存中的變量,工作內(nèi)存中存儲(chǔ)著主內(nèi)存中的變量副本拷貝,前面說過,工作內(nèi)存是每個(gè)線程的私有數(shù)據(jù)區(qū)域,因此不同的線程間無法訪問對(duì)方的工作內(nèi)存,線程間的通信(傳值)必須通過主內(nèi)存來完成。

14、如何保證多線程下 i++ 結(jié)果正確?~1-使用循環(huán)CAS,實(shí)現(xiàn)i++原子操作?

2-使用ReentrantLock 鎖機(jī)制,實(shí)現(xiàn)i++原子操作

3-使用synchronized,實(shí)現(xiàn)i++原子操作

15、線程池的種類,區(qū)別和使用場景?~

1-newCachedThreadPool:

底層:返回ThreadPoolExecutor實(shí)例,corePoolSize為0;maximumPoolSize為Integer.MAX_VALUE;keepAliveTime為60L;unit為TimeUnit.SECONDS;workQueue為SynchronousQueue(同步隊(duì)列)

通俗:當(dāng)有新任務(wù)到來,則插入到SynchronousQueue中,由于SynchronousQueue是同步隊(duì)列,因此會(huì)在池中尋找可用線程來執(zhí)行,若有可以線程則執(zhí)行,若沒有可用線程則創(chuàng)建一個(gè)線程來執(zhí)行該任務(wù);若池中線程空閑時(shí)間超過指定大小,則該線程會(huì)被銷毀。

適用:執(zhí)行很多短期異步的小程序或者負(fù)載較輕的服務(wù)器

2-newFixedThreadPool:

底層:返回ThreadPoolExecutor實(shí)例,接收參數(shù)為所設(shè)定線程數(shù)量nThread,corePoolSize為nThread,maximumPoolSize為nThread;keepAliveTime為0L(不限時(shí));unit為:TimeUnit.MILLISECONDS;WorkQueue為:new LinkedBlockingQueue<Runnable>()?無解阻塞隊(duì)列

通俗:創(chuàng)建可容納固定數(shù)量線程的池子,每隔線程的存活時(shí)間是無限的,當(dāng)池子滿了就不在添加線程了;如果池中的所有線程均在繁忙狀態(tài),對(duì)于新任務(wù)會(huì)進(jìn)入阻塞隊(duì)列中(無界的阻塞隊(duì)列)

適用:執(zhí)行長期的任務(wù),性能好很多

3-newSingleThreadExecutor:

底層:FinalizableDelegatedExecutorService包裝的ThreadPoolExecutor實(shí)例,corePoolSize為1;maximumPoolSize為1;keepAliveTime為0L;unit為:TimeUnit.MILLISECONDS;workQueue為:new LinkedBlockingQueue<Runnable>()?無解阻塞隊(duì)列

通俗:創(chuàng)建只有一個(gè)線程的線程池,且線程的存活時(shí)間是無限的;當(dāng)該線程正繁忙時(shí),對(duì)于新任務(wù)會(huì)進(jìn)入阻塞隊(duì)列中(無界的阻塞隊(duì)列)

適用:一個(gè)任務(wù)一個(gè)任務(wù)執(zhí)行的場景

4-NewScheduledThreadPool:

底層:創(chuàng)建ScheduledThreadPoolExecutor實(shí)例,corePoolSize為傳遞來的參數(shù),maximumPoolSize為Integer.MAX_VALUE;keepAliveTime為0;unit為:TimeUnit.NANOSECONDS;workQueue為:new DelayedWorkQueue()?一個(gè)按超時(shí)時(shí)間升序排序的隊(duì)列

通俗:創(chuàng)建一個(gè)固定大小的線程池,線程池內(nèi)線程存活時(shí)間無限制,線程池可以支持定時(shí)及周期性任務(wù)執(zhí)行,如果所有線程均處于繁忙狀態(tài),對(duì)于新任務(wù)會(huì)進(jìn)入DelayedWorkQueue隊(duì)列中,這是一種按照超時(shí)時(shí)間排序的隊(duì)列結(jié)構(gòu)

適用:周期性執(zhí)行任務(wù)的場景

16、分析線程池的實(shí)現(xiàn)原理和線程的調(diào)度過程?~說白了實(shí)現(xiàn)原理就是一個(gè)線程集合workerSet和一個(gè)阻塞隊(duì)列workQueue。當(dāng)用戶向線程池提交一個(gè)任務(wù)(也就是線程)時(shí),線程池會(huì)先將任務(wù)放入workQueue中。workerSet中的線程會(huì)不斷的從workQueue中獲取線程然后執(zhí)行。當(dāng)workQueue中沒有任務(wù)的時(shí)候,worker就會(huì)阻塞,直到隊(duì)列中有任務(wù)了就取出來繼續(xù)執(zhí)行。

17、線程池如何調(diào)優(yōu),最大數(shù)目如何確認(rèn)?~設(shè)置最大線程數(shù)

對(duì)于給定硬件上的給定負(fù)載,最大線程數(shù)設(shè)置為多少最好呢?這個(gè)問題回答起來并不簡單:它取決于負(fù)載特性以及底層硬件。特別是,最優(yōu)線程數(shù)還與每個(gè)任務(wù)阻塞的頻率有關(guān)。

假設(shè)JVM有4個(gè)CPU可用,很明顯最大線程數(shù)至少要設(shè)置為4。的確,除了處理這些任務(wù),JVM還有些線程要做其他的事,但是它們幾乎從來不會(huì)占用一個(gè)完整的CPU,至于這個(gè)數(shù)值是否要大于4,則需要進(jìn)行大量充分的測試。

有以下兩點(diǎn)需要注意:

一旦服務(wù)器成為瓶頸,向服務(wù)器增加負(fù)載時(shí)非常有害的;

對(duì)于CPU密集型或IO密集型的機(jī)器增加線程數(shù)實(shí)際會(huì)降低整體的吞吐量;

設(shè)置最小線程數(shù)

一旦確定了線程池的最大線程數(shù),就該確定所需的最小線程數(shù)了。大部分情況下,開發(fā)者會(huì)直截了當(dāng)?shù)膶⑺麄冊(cè)O(shè)置成同一個(gè)值。

將最小線程數(shù)設(shè)置為其他某個(gè)值(比如1),出發(fā)點(diǎn)是為了防止系統(tǒng)創(chuàng)建太多線程,以節(jié)省系統(tǒng)資源。指定一個(gè)最小線程數(shù)的負(fù)面影響相當(dāng)小。如果第一次就有很多任務(wù)要執(zhí)行,會(huì)有負(fù)面影響:這是線程池需要?jiǎng)?chuàng)建一個(gè)新線程。創(chuàng)建線程對(duì)性能不利,這也是為什么起初需要線程池的原因。

一般而言,對(duì)于線程數(shù)為最小值的線程池,一個(gè)新線程一旦創(chuàng)建出來,至少應(yīng)該保留幾分鐘,以處理任何負(fù)載飆升??臻e時(shí)間應(yīng)該以分鐘計(jì),而且至少在10分鐘到30分鐘之間,這樣可以防止頻繁創(chuàng)建線程。

線程池任務(wù)大小

等待線程池來執(zhí)行的任務(wù)會(huì)被保存到某個(gè)隊(duì)列或列表中;當(dāng)池中有線程可以執(zhí)行任務(wù)時(shí),就從隊(duì)列中拉出一個(gè)。這會(huì)導(dǎo)致不均衡:隊(duì)列中任務(wù)的數(shù)量可能變得非常大。如果隊(duì)列太大,其中的任務(wù)就必須等待很長時(shí)間,直到前面的任務(wù)執(zhí)行完畢。

對(duì)于任務(wù)隊(duì)列,線程池通常會(huì)限制其大小。但是這個(gè)值應(yīng)該如何調(diào)優(yōu),并沒有一個(gè)通用的規(guī)則。若要確定哪個(gè)值能帶來我們需要的性能,測量我們的真實(shí)應(yīng)用是唯一的途徑。不管是哪種情況,如果達(dá)到了隊(duì)列限制,再添加任務(wù)就會(huì)失敗。ThreadPoolExecutor有一個(gè)rejectedExecution方法,用于處理這種情況,默認(rèn)會(huì)拋出RejectedExecutionExecption。應(yīng)用服務(wù)器會(huì)向用戶返回某個(gè)錯(cuò)誤:或者是HTTP狀態(tài)碼500,或者是Web服務(wù)器捕獲異常錯(cuò)誤,并向用戶給出合理的解釋消息—其中后者是最理想的。

設(shè)置ThreadPoolExecutor的大小

線程池的一般行為是這樣的:創(chuàng)建時(shí)準(zhǔn)備最小數(shù)目的線程,如果來了一個(gè)任務(wù),而此時(shí)所有的線程都在忙碌,則啟動(dòng)一個(gè)新線程(一直到達(dá)到最大線程數(shù)),任務(wù)就會(huì)立即執(zhí)行。否則,任務(wù)被加入到等待隊(duì)列,如果隊(duì)列中已經(jīng)無法加入新任務(wù),則拒接之。

根據(jù)所選任務(wù)隊(duì)列的類型,ThreadPoolExecutor會(huì)決定何時(shí)會(huì)啟動(dòng)一個(gè)新線程。有以下三種可能:

SynchronousQueue

如果ThreadPoolExecutor搭配的是SynchronousQueue,則線程池的行為和我們預(yù)期的一樣,它會(huì)考慮線程數(shù):如果所有的線程都在忙碌,而且池中的線程數(shù)尚未達(dá)到最大,則會(huì)為新任務(wù)啟動(dòng)一個(gè)新線程。然而這個(gè)隊(duì)列沒辦法保存等待的任務(wù):如果來了一個(gè)任務(wù),創(chuàng)建的線程數(shù)已經(jīng)達(dá)到最大值,而且所有的線程都在忙碌,則新的任務(wù)都會(huì)被拒絕,所以如果是管理少量的任務(wù),這是個(gè)不錯(cuò)的選擇,對(duì)于其他的情況就不適合了。

無界隊(duì)列

如果ThreadPoolExecutor搭配的是無界隊(duì)列,如LinkedBlockingQueue,則不會(huì)拒絕任何任務(wù)(因?yàn)殛?duì)列大小沒有限制)。這種情況下,ThreadPoolExecutor最多僅會(huì)按照最小線程數(shù)創(chuàng)建線程,也就是說最大線程池大小被忽略了。如果最大線程數(shù)和最小線程數(shù)相同,則這種選擇和配置了固定線程數(shù)的傳統(tǒng)線程池運(yùn)行機(jī)制最為接近。

有界隊(duì)列

搭配了有界隊(duì)列,如ArrayBlockingQueue的ThreadPoolExecutor會(huì)采用一個(gè)非常負(fù)責(zé)的算法。比如假定線程池的最小線程數(shù)為4,最大為8所用的ArrayBlockingQueue最大為10。隨著任務(wù)到達(dá)并被放到隊(duì)列中,線程池中最多運(yùn)行4個(gè)線程(即最小線程數(shù))。即使隊(duì)列完全填滿,也就是說有10個(gè)處于等待狀態(tài)的任務(wù),ThreadPoolExecutor也只會(huì)利用4個(gè)線程。

如果隊(duì)列已滿,而又有新任務(wù)進(jìn)來,此時(shí)才會(huì)啟動(dòng)一個(gè)新線程,這里不會(huì)因?yàn)殛?duì)列已滿而拒接該任務(wù),相反會(huì)啟動(dòng)一個(gè)新線程。新線程會(huì)運(yùn)行隊(duì)列中的第一個(gè)任務(wù),為新來的任務(wù)騰出空間。

這個(gè)算法背后的理念是:該池大部分時(shí)間僅使用核心線程(4個(gè)),即使有適量的任務(wù)在隊(duì)列中等待運(yùn)行。這時(shí)線程池就可以用作節(jié)流閥。如果擠壓的請(qǐng)求變得非常多,這時(shí)該池就會(huì)嘗試運(yùn)行更多的線程來清理;這時(shí)第二個(gè)節(jié)流閥—最大線程數(shù)就起作用了。

18、ThreadLocal原理,用的時(shí)候需要注意什么?~ThreadLoal 變量,它的基本原理是,同一個(gè) ThreadLocal 所包含的對(duì)象(對(duì)ThreadLocal< String >而言即為 String 類型變量),在不同的 Thread 中有不同的副本(實(shí)際是不同的實(shí)例,后文會(huì)詳細(xì)闡述)。這里有幾點(diǎn)需要注意

因?yàn)槊總€(gè) Thread 內(nèi)有自己的實(shí)例副本,且該副本只能由當(dāng)前 Thread 使用。這是也是 ThreadLocal 命名的由來

既然每個(gè) Thread 有自己的實(shí)例副本,且其它 Thread 不可訪問,那就不存在多線程間共享的問題

總的來說,ThreadLocal 適用于每個(gè)線程需要自己獨(dú)立的實(shí)例且該實(shí)例需要在多個(gè)方法中被使用,也即變量在線程間隔離而在方法或類間共享的場景。后文會(huì)通過實(shí)例詳細(xì)闡述該觀點(diǎn)。另外,該場景下,并非必須使用 ThreadLocal ,其它方式完全可以實(shí)現(xiàn)同樣的效果,只是 ThreadLocal 使得實(shí)現(xiàn)更簡潔。

Spring

1、BeanFactory 和 FactoryBean?~BeanFactory是IOC最基本的容器,負(fù)責(zé)生產(chǎn)和管理bean,它為其他具體的IOC容器提供了最基本的規(guī)范;

FactoryBean是一個(gè)接口,當(dāng)在IOC容器中的Bean實(shí)現(xiàn)了FactoryBean后,通過getBean(String BeanName)獲取到的Bean對(duì)象并不是FactoryBean的實(shí)現(xiàn)類對(duì)象,而是這個(gè)實(shí)現(xiàn)類中的getObject()方法返回的對(duì)象;

2、Spring IOC 的理解,其初始化過程?~I(xiàn)oC文英全稱Inversion of Control,即控制反轉(zhuǎn),我么可以這么理解IoC容器:

  “把某些業(yè)務(wù)對(duì)象的的控制權(quán)交給一個(gè)平臺(tái)或者框架來同一管理,這個(gè)同一管理的平臺(tái)可以稱為IoC容器。”

1-Resource定位(Bean的定義文件定位)

2-將Resource定位好的資源載入到BeanDefinition

3-將BeanDefiniton注冊(cè)到容器中

3、BeanFactory 和 ApplicationContext?~BeanFactory 是 Spring 的“心臟”。它就是 Spring IoC 容器的真面目。Spring 使用 BeanFactory 來實(shí)例化、配置和管理 Bean。

如果說BeanFactory是Spring的心臟,那么ApplicationContext就是完整的軀體了,ApplicationContext由BeanFactory派生而來,提供了更多面向?qū)嶋H應(yīng)用的功能。在BeanFactory中,很多功能需要以編程的方式實(shí)現(xiàn),而在ApplicationContext中則可以通過配置實(shí)現(xiàn)。

4、Spring Bean 的生命周期,如何被管理的?~1. 實(shí)例化一個(gè)Bean,也就是我們通常說的new

2. 按照Spring上下文對(duì)實(shí)例化的Bean進(jìn)行配置,也就是IOC注入

3. 如果這個(gè)Bean實(shí)現(xiàn)了BeanNameAware接口,會(huì)調(diào)用它實(shí)現(xiàn)的setBeanName(String beanId)方法,此處傳遞的是Spring配置文件中Bean的ID

4. 如果這個(gè)Bean實(shí)現(xiàn)了BeanFactoryAware接口,會(huì)調(diào)用它實(shí)現(xiàn)的setBeanFactory(),傳遞的是Spring工廠本身(可以用這個(gè)方法獲取到其他Bean)

5. 如果這個(gè)Bean實(shí)現(xiàn)了ApplicationContextAware接口,會(huì)調(diào)用setApplicationContext(ApplicationContext)方法,傳入Spring上下文,該方式同樣可以實(shí)現(xiàn)步驟4,但比4更好,以為ApplicationContext是BeanFactory的子接口,有更多的實(shí)現(xiàn)方法

6. 如果這個(gè)Bean關(guān)聯(lián)了BeanPostProcessor接口,將會(huì)調(diào)用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor經(jīng)常被用作是Bean內(nèi)容的更改,并且由于這個(gè)是在Bean初始化結(jié)束時(shí)調(diào)用After方法,也可用于內(nèi)存或緩存技術(shù)

7. 如果這個(gè)Bean在Spring配置文件中配置了init-method屬性會(huì)自動(dòng)調(diào)用其配置的初始化方法

8. 如果這個(gè)Bean關(guān)聯(lián)了BeanPostProcessor接口,將會(huì)調(diào)用postAfterInitialization(Object obj, String s)方法

注意:以上工作完成以后就可以用這個(gè)Bean了,那這個(gè)Bean是一個(gè)single的,所以一般情況下我們調(diào)用同一個(gè)ID的Bean會(huì)是在內(nèi)容地址相同的實(shí)例

9. 當(dāng)Bean不再需要時(shí),會(huì)經(jīng)過清理階段,如果Bean實(shí)現(xiàn)了DisposableBean接口,會(huì)調(diào)用其實(shí)現(xiàn)的destroy方法

10. 最后,如果這個(gè)Bean的Spring配置中配置了destroy-method屬性,會(huì)自動(dòng)調(diào)用其配置的銷毀方法

5、Spring Bean 的加載過程是怎樣的?~1. 加載XML文件,封裝成Resource對(duì)象

2. 調(diào)用Reader對(duì)象方法讀取XML文件內(nèi)容,并將相關(guān)屬性放到BeanDefinition實(shí)例

3. 將BeanDefinition對(duì)象放到BeanFactory對(duì)象

6、如果要你實(shí)現(xiàn)Spring AOP,請(qǐng)問怎么實(shí)現(xiàn)?7、如果要你實(shí)現(xiàn)Spring IOC,你會(huì)注意哪些問題?8、Spring 是如何管理事務(wù)的,事務(wù)管理機(jī)制?~Spring事務(wù)管理高層抽象主要包括3個(gè)接口,Spring的事務(wù)主要是由他們共同完成的:

PlatformTransactionManager:事務(wù)管理器—主要用于平臺(tái)相關(guān)事務(wù)的管理

TransactionDefinition:? ? 事務(wù)定義信息(隔離、傳播、超時(shí)、只讀)—通過配置如何進(jìn)行事務(wù)管理。

TransactionStatus:事務(wù)具體運(yùn)行狀態(tài)—事務(wù)管理過程中,每個(gè)時(shí)間點(diǎn)事務(wù)的狀態(tài)信息。

9、Spring 的不同事務(wù)傳播行為有哪些,干什么用的?~1、PROPAGATION_REQUIRED:如果當(dāng)前沒有事務(wù),就創(chuàng)建一個(gè)新事務(wù),如果當(dāng)前存在事務(wù),就加入該事務(wù),該設(shè)置是最常用的設(shè)置。

2、PROPAGATION_SUPPORTS:支持當(dāng)前事務(wù),如果當(dāng)前存在事務(wù),就加入該事務(wù),如果當(dāng)前不存在事務(wù),就以非事務(wù)執(zhí)行。‘

3、PROPAGATION_MANDATORY:支持當(dāng)前事務(wù),如果當(dāng)前存在事務(wù),就加入該事務(wù),如果當(dāng)前不存在事務(wù),就拋出異常。

4、PROPAGATION_REQUIRES_NEW:創(chuàng)建新事務(wù),無論當(dāng)前存不存在事務(wù),都創(chuàng)建新事務(wù)。

5、PROPAGATION_NOT_SUPPORTED:以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起。

6、PROPAGATION_NEVER:以非事務(wù)方式執(zhí)行,如果當(dāng)前存在事務(wù),則拋出異常。

7、PROPAGATION_NESTED:如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行。如果當(dāng)前沒有事務(wù),則執(zhí)行與PROPAGATION_REQUIRED類似的操作。

10、Spring 中用到了那些設(shè)計(jì)模式?~1-簡單工廠

2-工廠方法

3-單例模式

4-適配器

5-包裝器

6-代理

7-觀察者

8-策略

9-模板方法

分布式相關(guān)1、Dubbo的底層實(shí)現(xiàn)原理和機(jī)制~1.???client一個(gè)線程調(diào)用遠(yuǎn)程接口,生成一個(gè)唯一的ID(比如一段隨機(jī)字符串,UUID等),Dubbo是使用AtomicLong從0開始累計(jì)數(shù)字的

2.???將打包的方法調(diào)用信息(如調(diào)用的接口名稱,方法名稱,參數(shù)值列表等),和處理結(jié)果的回調(diào)對(duì)象callback,全部封裝在一起,組成一個(gè)對(duì)象object

3.???向?qū)iT存放調(diào)用信息的全局ConcurrentHashMap里面put(ID, object)

4.???將ID和打包的方法調(diào)用信息封裝成一對(duì)象connRequest,使用IoSession.write(connRequest)異步發(fā)送出去

5.???當(dāng)前線程再使用callback的get()方法試圖獲取遠(yuǎn)程返回的結(jié)果,在get()內(nèi)部,則使用synchronized獲取回調(diào)對(duì)象callback的鎖,?再先檢測是否已經(jīng)獲取到結(jié)果,如果沒有,然后調(diào)用callback的wait()方法,釋放callback上的鎖,讓當(dāng)前線程處于等待狀態(tài)。

6.???服務(wù)端接收到請(qǐng)求并處理后,將結(jié)果(此結(jié)果中包含了前面的ID,即回傳)發(fā)送給客戶端,客戶端socket連接上專門監(jiān)聽消息的線程收到消息,分析結(jié)果,取到ID,再從前面的ConcurrentHashMap里面get(ID),從而找到callback,將方法調(diào)用結(jié)果設(shè)置到callback對(duì)象里。

7.???監(jiān)聽線程接著使用synchronized獲取回調(diào)對(duì)象callback的鎖(因?yàn)榍懊嬲{(diào)用過wait(),那個(gè)線程已釋放callback的鎖了),再notifyAll(),喚醒前面處于等待狀態(tài)的線程繼續(xù)執(zhí)行(callback的get()方法繼續(xù)執(zhí)行就能拿到調(diào)用結(jié)果了),至此,整個(gè)過程結(jié)束。

2、描述一個(gè)服務(wù)從發(fā)布到被消費(fèi)的詳細(xì)過程3、分布式系統(tǒng)怎么做服務(wù)治理~單provider,多cusumer,服務(wù)的注冊(cè)和訂閱

4、接口的冪等性的概念~多次操作返回同一結(jié)果

5、消息中間件如何解決消息丟失問題~事物,持久化,取消自動(dòng)ack

6、Dubbo的服務(wù)請(qǐng)求失敗怎么處理~查看管控臺(tái)

查看注解?

查看掃描包?

查看發(fā)布服務(wù)?

查看加載配置文件

7、重連機(jī)制會(huì)不會(huì)造成錯(cuò)誤~不當(dāng)?shù)闹剡B有可能會(huì)造成雪崩

8、對(duì)分布式事務(wù)的理解~原子性,一致性,隔離性,持久性

9、如何實(shí)現(xiàn)負(fù)載均衡,有哪些算法可以實(shí)現(xiàn)?

10、Zookeeper的用途,選舉的原理是什么?

11、數(shù)據(jù)的垂直拆分水平拆分。

12、zookeeper原理和適用場景

13、zookeeper watch機(jī)制

14、redis/zk節(jié)點(diǎn)宕機(jī)如何處理

15、分布式集群下如何做到唯一序列號(hào)

16、如何做一個(gè)分布式鎖

緩存

1、Redis用過哪些數(shù)據(jù)數(shù)據(jù),以及Redis底層怎么實(shí)現(xiàn)

2、Redis緩存穿透,緩存雪崩

3、如何使用Redis來實(shí)現(xiàn)分布式鎖

4、Redis的并發(fā)競爭問題如何解決

5、Redis持久化的幾種方式,優(yōu)缺點(diǎn)是什么,怎么實(shí)現(xiàn)的

6、Redis的緩存失效策略

7、Redis集群,高可用,原理

8、Redis緩存分片

9、Redis的數(shù)據(jù)淘汰策略

JVM

1、詳細(xì)jvm內(nèi)存模型

2、講講什么情況下回出現(xiàn)內(nèi)存溢出,內(nèi)存泄漏?

3、說說Java線程棧

4、JVM 年輕代到年老代的晉升過程的判斷條件是什么呢?

5、JVM 出現(xiàn) fullGC 很頻繁,怎么去線上排查問題?

6、類加載為什么要使用雙親委派模式,有沒有什么場景是打破了這個(gè)模式?

7、類的實(shí)例化順序

8、JVM垃圾回收機(jī)制,何時(shí)觸發(fā)MinorGC等操作

9、JVM 中一次完整的 GC 流程(從 ygc 到 fgc)是怎樣的

10、各種回收器,各自優(yōu)缺點(diǎn),重點(diǎn)CMS、G1

11、各種回收算法

12、OOM錯(cuò)誤,stackoverflow錯(cuò)誤,permgen space錯(cuò)誤

~OOM-OutOfMemory-堆內(nèi)存溢出

stackoverflow-棧內(nèi)存溢出

permgen space-永久空間

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 不足的地方請(qǐng)大家多多指正,如有其它沒有想到的常問面試題請(qǐng)大家多多評(píng)論,一起成長,感謝!~ String可以被繼承嗎...
    啟示錄是真的閱讀 3,073評(píng)論 3 3
  • 在一個(gè)方法內(nèi)部定義的變量都存儲(chǔ)在棧中,當(dāng)這個(gè)函數(shù)運(yùn)行結(jié)束后,其對(duì)應(yīng)的棧就會(huì)被回收,此時(shí),在其方法體中定義的變量將不...
    Y了個(gè)J閱讀 4,575評(píng)論 1 14
  • 01:項(xiàng)目介紹 此乃面試之時(shí)第一環(huán)節(jié),主要考察你的概述能力和對(duì)業(yè)務(wù)的熟悉程度, 對(duì)需求的分析能力,溝通協(xié)作能力,產(chǎn)...
    小小弓長張閱讀 1,162評(píng)論 0 0
  • 假期忙里偷閑, 重新看了《父母愛情》 感覺安杰果真是活的太有成就感。 出身富裕,衣食無憂,受過良好教育, 花容月貌...
    輕輕道來閱讀 294評(píng)論 0 0
  • 2018/9/22 星期六 天氣晴朗 -1- 最近幾天,我一直在耳邊重復(fù)白百何...
    _溫言_閱讀 1,110評(píng)論 10 13

友情鏈接更多精彩內(nèi)容