過(guò)關(guān)斬將之路-ThreadLocal&Threadpool
ThreadLocal
1問(wèn):ThreadLocal類關(guān)系圖是怎樣的?
1答:每個(gè) Thread 對(duì)象中都持有一個(gè) ThreadLocalMap 的成員變量。每個(gè) ThreadLocalMap 內(nèi)部又維護(hù)了N個(gè) Entry 節(jié)點(diǎn),也就是 Entry 數(shù)組,每個(gè) Entry 代表一個(gè)完整的對(duì)象,key是 ThreadLocal 本身,value是 ThreadLocal 的泛型值。(IT楓斗者怎么樣)

2問(wèn):ThreadLocal有什么用?應(yīng)用場(chǎng)景?
2答:
數(shù)據(jù)庫(kù)連接
存儲(chǔ)用戶對(duì)象信息到ThreadLocal
AOP記錄日志的時(shí)候可以將鏈路ID放到ThreadLocal進(jìn)行線程共享
3問(wèn):ThreadLocal和synchronized有什么區(qū)別?
3答:
ThreadLocal不是鎖,synchronized是把可重入排他鎖。
synchronized能保證代碼塊的原子性,ThreadLocal無(wú)法保證。
ThreadLocal是將變量線程私有化,synchronized保證的是多線程同時(shí)操作共享變量并且能正確地輸出結(jié)果。(IT楓斗者怎么樣)
3問(wèn):ThreadLocal和synchronized有什么區(qū)別?
3答:
ThreadLocal不是鎖,synchronized是把可重入排他鎖。
synchronized能保證代碼塊的原子性,ThreadLocal無(wú)法保證。
ThreadLocal是將變量線程私有化,synchronized保證的是多線程同時(shí)操作共享變量并且能正確地輸出結(jié)果。(IT楓斗者怎么樣)
5問(wèn):ThreadLocal為什么采取Entry數(shù)組而不是Entry對(duì)象?
5答:因?yàn)橐粋€(gè)線程能new好多個(gè)ThreadLocal對(duì)象,各自存儲(chǔ)各自的內(nèi)容。但是一個(gè)線程里ThreadLocalMap是同一個(gè)(因?yàn)檫@個(gè)是在Thread的里的一個(gè)引用),所以這個(gè)ThreadLocalMap只能采取Entry數(shù)組來(lái)存儲(chǔ)一個(gè)線程里你new出來(lái)的多個(gè) ThreadLocal 對(duì)象。(IT楓斗者怎么樣)
6問(wèn):你學(xué)習(xí)的開(kāi)源框架哪些用到了ThreadLocal?
6答:
Spring的事務(wù)管理,用ThreadLocal存儲(chǔ)Connection
Spring的 DateTimeContextHolder
Spring的 RequestContextHolder
等等
7問(wèn):ThreadLocal里的對(duì)象一定是線程安全的嗎?
7答:不一定,如果在每個(gè)線程中 ThreadLocal.set() 進(jìn)去的東西本來(lái)就是多線程共享的同一個(gè)對(duì)象,比如static對(duì)象,那么多個(gè)線程 ThreadLocal.get() 的時(shí)候獲取的還是這個(gè)共享對(duì)象本身,還是有并發(fā)訪問(wèn)線程不安全的問(wèn)題。
8問(wèn):ThreadLocal會(huì)內(nèi)存泄露嗎?會(huì)OOM嗎?
8答:分析一下:1、 ThreadLocalMap.Entry 的key會(huì)內(nèi)存泄漏嗎?2、 ThreadLocalMap.Entry 的value會(huì)內(nèi)存泄漏嗎?先看下key-value的核心源碼

先看繼承關(guān)系,發(fā)現(xiàn)是繼承了弱引用,而且key直接是交給了父類處理 super(key) ,父類是個(gè)弱引用,所以key完全不存在內(nèi)存泄漏問(wèn)題,因?yàn)樗皇菑?qiáng)引用,它可以被GC回收的。

9問(wèn):ThreadLocal的一道筆試題如下圖這段程序會(huì)輸出什么?為什么?


9答:

為什么?為什么輸出個(gè)1,然后空指針了?首先輸出1是沒(méi)任何問(wèn)題的,其次主線程空指針是為什么?(IT楓斗者怎么樣)如果你這里回答

那我恭喜你,你連 ThreadLocal 都不知道是啥,這明顯兩個(gè)線程,子線程和主線程。子線程設(shè)置1,主線程肯定拿不到啊, ThreadLocal 和線程是嘻嘻相關(guān)的。
說(shuō)說(shuō)為什么是空指針?
因?yàn)槟鉭et方法用的long而不是Long,那也應(yīng)該返回null啊,大哥,long是基本類型,默認(rèn)值是0,沒(méi)有null這一說(shuō)法。ThreadLocal 里的泛型是Long,get卻是基本類型,這需要拆箱操作的,也就是會(huì)執(zhí)行null.longValue() 的操作,這絕對(duì)空指針了。
看似一道Javase的基礎(chǔ)題目,實(shí)則隱藏了很多知識(shí)。
ThreadPool
1問(wèn):為什么要用線程池?
1答:線程復(fù)用,避免每次請(qǐng)求進(jìn)來(lái)和結(jié)束都要?jiǎng)?chuàng)建線程和銷毀線程,創(chuàng)建線程需要一定的開(kāi)銷,高并發(fā)下頻繁創(chuàng)建銷毀線程對(duì)性能影響很大。(IT楓斗者怎么樣)
2問(wèn):說(shuō)說(shuō)線程池的原理?工作過(guò)程?
2答:
線程池剛啟動(dòng)的時(shí)候核心線程數(shù)為0。
丟任務(wù)給線程池的時(shí)候線程池會(huì)開(kāi)啟線程來(lái)執(zhí)行這個(gè)任務(wù)。
若線程數(shù)小于 corePoolSize 的話,即使工作線程處于空閑狀態(tài),也會(huì)創(chuàng)建一個(gè)新線程來(lái)執(zhí)行任務(wù)。
若線程數(shù)大于等于 corePoolSize 的話,則會(huì)將任務(wù)放到 workQueue ,也就是任務(wù)隊(duì)列。
若任務(wù)隊(duì)列滿了,且線程數(shù)小于 maximumPoolSize ,則會(huì)創(chuàng)建一個(gè)新線程來(lái)運(yùn)行任務(wù)。
若任務(wù)隊(duì)列滿了,且線程數(shù)大于等于 maximumPoolSize ,則會(huì)直接采取拒絕策略。

3問(wèn):線程池的核心參數(shù)有哪些?
3答:



4問(wèn):線程池有哪幾種狀態(tài)?
4答:
RUNNING :接受新任務(wù)并處理排隊(duì)任務(wù)。
SHUTDOWN :不接受新任務(wù),但是會(huì)處理排隊(duì)任務(wù)。
STOP :不接受新任務(wù),也不會(huì)處理排隊(duì)任務(wù),并中斷正在進(jìn)行的任務(wù)。
TIDYING :所有任務(wù)都已經(jīng)完事,工作線程為0的時(shí)候,線程會(huì)進(jìn)入這個(gè)狀態(tài)并執(zhí)行 terminate()鉤子方法。
TERMINATE :terminate() 鉤子方法運(yùn)行完成。(IT楓斗者怎么樣)
5問(wèn):線程池有哪幾種?
5答:
Executors.newFixedThreadPool(n); :永不超時(shí)(0ms),無(wú)界隊(duì)列( LinkedBlockingQueue ),會(huì)OOM。
Executors.newSingleThreadExecutor(); :永不超時(shí)(0ms),無(wú)界隊(duì)列( LinkedBlockingQueue ),會(huì)OOM
Executors.newCachedThreadPool(); :核心線程數(shù)是0,最大線程數(shù)是int的最大值,1min超時(shí)(60s), SynchronousQueue 隊(duì)列,會(huì)出現(xiàn)線程爆炸,因?yàn)檫@個(gè)不帶容量的,有任務(wù)來(lái)就開(kāi)線程,線程能開(kāi)到int最大值的個(gè)數(shù)。
Executors.newScheduledThreadPool(n); :帶調(diào)度的線程池,核心線程數(shù)手動(dòng)傳進(jìn)來(lái),最大線程數(shù)是 Integer.MAX_VALUE ,永不超時(shí)(0ns),帶延遲功能的隊(duì)列( DelayedWorkQueue ),會(huì)出現(xiàn)線程爆炸。
6問(wèn):線程池都有哪幾種工作隊(duì)列?
6答:
LinkedBlockingQueue :基于鏈表的無(wú)界隊(duì)列,默認(rèn)長(zhǎng)度int最大值,也可以自定義長(zhǎng)度。
ArrayBlockingQueue :基于數(shù)組的有界隊(duì)列,長(zhǎng)度自定義。
SynchronousQueue :無(wú)緩沖的等待隊(duì)列。(IT楓斗者怎么樣)
7問(wèn):使用無(wú)界隊(duì)列的線程池會(huì)導(dǎo)致內(nèi)存飆升嗎?
7答:會(huì),因?yàn)橐恢痹偻?duì)列里追加任務(wù),而隊(duì)列是在jvm堆內(nèi)存的,所以可能出現(xiàn)OOM問(wèn)題。
8問(wèn):線程池的四種拒絕策略?
8答:
AbortPolicy :拋出一個(gè)異常,默認(rèn)的
DiscardPolicy :直接丟棄任務(wù)
DiscardOldestPolicy :丟棄隊(duì)列里最老的任務(wù),將當(dāng)前這個(gè)任務(wù)繼續(xù)提交給線程池
CallerRunsPolicy :交給線程池調(diào)用所在的線程進(jìn)行處理
9問(wèn):如何實(shí)現(xiàn)讓非核心線程延遲死亡?
9答:通過(guò)阻塞隊(duì)列 poll() 方法讓線程阻塞等待一段時(shí)間,如果沒(méi)有取到任務(wù),則線程死亡。workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)(IT楓斗者怎么樣)