面試官因?yàn)榫€程池,讓我出門左拐!
前幾天阿粉的朋友面試,在面試的時(shí)候,面試官問(wèn)到了框架,項(xiàng)目,JVM還有一些關(guān)于線程池的內(nèi)容,而這個(gè)線程池,讓阿粉的朋友分分鐘被面試官吊打,只能出門左拐,地鐵站回家了。為什么呢?因?yàn)榫€程池他是真的沒(méi)有下功夫去準(zhǔn)備,只能涼涼了。
前序說(shuō)實(shí)話,阿粉在面試的時(shí)候,最開(kāi)始的時(shí)候的面試,面試官只是會(huì)問(wèn)實(shí)現(xiàn)多線程的方式都有哪些,但是你說(shuō)到關(guān)于線程池的內(nèi)容的時(shí)候,都是一句帶過(guò),而有些面試官對(duì)這個(gè)也不是很細(xì)抓,但是自從阿里的面試官開(kāi)始問(wèn)關(guān)于線程池的問(wèn)題之后,這個(gè)問(wèn)題就成了高頻熱點(diǎn)了。
那么接下來(lái),阿粉就繼續(xù)帶給大家關(guān)于這個(gè)線程池,如何分分鐘擺平面試官。
1.什么是線程池
java.util.concurrent.Executors 這個(gè)類大家不知道有沒(méi)有仔細(xì)的去看過(guò)這個(gè),而這個(gè)類中給我提供了很多方法來(lái)創(chuàng)建線程池。
在代碼的開(kāi)頭的注釋上就寫明了,它可以創(chuàng)建重復(fù)使用固定數(shù)量線程的線程池,如果在所有線程都處于活動(dòng)狀態(tài)時(shí)提交了其他任務(wù),那么他們將在隊(duì)列中等待線程可用。
- public static ExecutorService newFixedThreadPool(int nThreads) {
- return new ThreadPoolExecutor(nThreads, nThreads,
- 0L, TimeUnit.MILLISECONDS,
- new LinkedBlockingQueue<Runnable>());
- }
而我們創(chuàng)建線程池就是為了解決處理器單元內(nèi)多個(gè)線程執(zhí)行的問(wèn)題,它可以顯著減少處理器單元的閑置時(shí)間,增加處理器單元的吞吐能力。
而面試的時(shí)候,我們肯定不能這么說(shuō),面試的時(shí)候我們可以這么說(shuō):
做Java的,當(dāng)然知道線程池,我們?cè)谧鲩_(kāi)發(fā)的時(shí)候有時(shí)候需要做的任務(wù)慢慢的增多,復(fù)雜性也會(huì)變得越來(lái)越強(qiáng),所以線程的個(gè)數(shù)就會(huì)一點(diǎn)點(diǎn)的往上增加,而對(duì)應(yīng)的線程占用的資源也就越來(lái)越多,多個(gè)線程占用資源的釋放與注銷需要維護(hù),這時(shí)候多個(gè)線程的管理就顯得有尤為重要。針對(duì)這一情況,sun公司提供了線程池,對(duì)線程集合的管理工具。所以線程池就出現(xiàn)了,接下來(lái)面試官的問(wèn)題就是比較狠了,你平常是怎么使用的,幾種常見(jiàn)的都有哪些,畢竟面試官的套路一環(huán)套一環(huán)。
2.常見(jiàn)的線程池都有哪些,使用的場(chǎng)景是哪里呢?
這時(shí)候這個(gè)java.util.concurrent.Executors 類大家就排上用場(chǎng)了,比如:
(1) newSingleThreadExecutor
- 單個(gè)線程的線程池,即線程池中每次只有一個(gè)線程工作,單線程串行執(zhí)行任務(wù)
- public ThreadPoolExecutor(int corePoolSize,
- int maximumPoolSize,
- long keepAliveTime,
- TimeUnit unit,
- BlockingQueue<Runnable> workQueue,
- ThreadFactory threadFactory) {
- this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
- threadFactory, defaultHandler);
- }
(2)newFixedThreadPool
下面的兩個(gè)方法是這個(gè)方法的重載,而它的意思很明確,建立一個(gè)線程數(shù)量固定的線程池,規(guī)定的最大線程數(shù)量,超過(guò)這個(gè)數(shù)量之后進(jìn)來(lái)的任務(wù),會(huì)放到等待隊(duì)列中,如果有空閑線程,則在等待隊(duì)列中獲取,遵循先進(jìn)先出原則。
- public static ExecutorService newFixedThreadPool(int nThreads) {
- return new ThreadPoolExecutor(nThreads, nThreads,
- 0L, TimeUnit.MILLISECONDS,
- new LinkedBlockingQueue<Runnable>());
- }
- public static ExecutorService newFixedThreadPool(int nThreads) {
- return new ThreadPoolExecutor(nThreads, nThreads,
- 0L, TimeUnit.MILLISECONDS,
- new LinkedBlockingQueue<Runnable>());
- }
(3)newCacheThreadExecutor
緩存型線程池,這個(gè)線程池的意思是在核心線程達(dá)到最大值之前,如果繼續(xù)有任務(wù)進(jìn)來(lái)就會(huì)創(chuàng)建新的核心線程,并加入核心線程池,即使有空閑的線程,也不會(huì)復(fù)用。
而達(dá)到最大核心線程數(shù)后,新任務(wù)進(jìn)來(lái),如果有空閑線程,則直接拿來(lái)使用,如果沒(méi)有空閑線程,則新建臨時(shí)線程.
而緩存型的線程池使用的是SynchronousQueue作為等待隊(duì)列,他不保存任何的任務(wù),新的任務(wù)加入進(jìn)來(lái)之后,他會(huì)創(chuàng)建臨時(shí)線程來(lái)進(jìn)行使用
- public static ExecutorService newCachedThreadPool() {
- return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
- 60L, TimeUnit.SECONDS,
- new SynchronousQueue<Runnable>());
- }
(4)newScheduledThreadPool
計(jì)劃型線程池,在它的注釋中給出的很明確的解釋,創(chuàng)建一個(gè)線程池,該線程池可以計(jì)劃在給定的延遲,或周期性地執(zhí)行。
也就是說(shuō),在新任務(wù)到達(dá)的時(shí)候,我們看到底有沒(méi)有空閑線程,如果有,直接拿來(lái)使用,如果沒(méi)有,則新建線程加入池。而這里面使用的就是DelayedWorkQueue作為等待隊(duì)列,中間進(jìn)行了一定的等待,等待時(shí)間過(guò)后,繼續(xù)執(zhí)行任務(wù)。
- public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
- return new ScheduledThreadPoolExecutor(corePoolSize);
- }
- public static ScheduledExecutorService newScheduledThreadPool(
- int corePoolSize, ThreadFactory threadFactory) {
- return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
- }
3.你看過(guò)阿里巴巴開(kāi)發(fā)手冊(cè)么?里面對(duì)線程是怎么說(shuō)的?說(shuō)實(shí)話,阿粉是一開(kāi)始真的沒(méi)怎么注意過(guò)這個(gè)在阿里巴巴開(kāi)發(fā)手冊(cè)上關(guān)于線程的使用,是怎么做的,而面試官很明顯,問(wèn)出這個(gè)問(wèn)題的時(shí)候,肯定是看過(guò)了,之后阿粉看了阿里巴巴開(kāi)發(fā)手冊(cè),不得不感慨,阿里巴巴,真的是..
我們?cè)谌粘J褂枚际菚?huì)出現(xiàn)這段代碼:
- ExecutorService cachedThreadPool=Executors.newFixedThreadPool();
但是阿里巴巴說(shuō),不好意思呀,強(qiáng)制線程池不允許使用 Executors 去創(chuàng)建
那你說(shuō)嘛,我該怎么辦,而推薦的卻是 ThreadPoolExecutor
- public ThreadPoolExecutor(int corePoolSize,
- int maximumPoolSize,
- long keepAliveTime,
- TimeUnit unit,
- BlockingQueue<Runnable> workQueue) {
- this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
- Executors.defaultThreadFactory(), defaultHandler);
- }
這個(gè)方法里面有幾個(gè)參數(shù)
- corePoolSize 要保留在池中的線程數(shù),也就是線程池核心池的大小
- maximumPoolSize 最大線程數(shù)
- keepAliveTime 當(dāng)線程數(shù)大于核心時(shí),此為終止前多余的空閑線程等待新任務(wù)的最長(zhǎng)時(shí)間。
- unit keepAliveTime 參數(shù)的時(shí)間單位
- workQueue 用來(lái)儲(chǔ)存等待執(zhí)行任務(wù)的隊(duì)列。
- threadFactory 線程工廠
- handler 默認(rèn)的拒絕執(zhí)行處理程序
而這些參數(shù)也是面試中經(jīng)常會(huì)問(wèn)到的呦,而如何選擇合適的線程池,如何合理的配置線程池大小,請(qǐng)繼續(xù)關(guān)注阿粉,阿粉將會(huì)在最近幾天帶個(gè)大家,點(diǎn)個(gè)再看再走唄