每日六道java新手入門(mén)面試題,通往自由的道路--線程池
1. 你知道線程池嗎?為什么需要使用線程池?
在面向?qū)ο缶幊讨校瑒?chuàng)建和銷毀對(duì)象是很費(fèi)時(shí)間的,因?yàn)閯?chuàng)建一個(gè)對(duì)象要獲取內(nèi)存資源或者其它更多資源。
而在Java中, JVM 中每創(chuàng)建和銷毀線程就需要資源和時(shí)間的損耗了,線程中也是存在上下文切換,這需要一定的開(kāi)銷,并且線程的創(chuàng)建并不是越多越好,而如果創(chuàng)建的線程數(shù)太多,上下文切換的頻率就變高,可能使得多線程帶來(lái)的好處抵不過(guò)線程切換帶來(lái)的開(kāi)銷,就有點(diǎn)得不償失了。
那我們需要如何管控好線程呢?
所以我們可以創(chuàng)建一個(gè)容器把線程數(shù)緩存在容器了,以便給他人使用,并且無(wú)需再自行創(chuàng)建和銷毀線程。
小結(jié):
線程池就是事先創(chuàng)建若干個(gè)可執(zhí)行的線程放入一個(gè)池(容器)中,需要的時(shí)候從池中獲取線程不用自行創(chuàng)建,使用完畢不需要銷毀線程而是放回池中,從而減少創(chuàng)建和銷毀。
使用線程池的好處:
- 降低了資源的消耗,重用存在的線程,減少線程的創(chuàng)建和銷毀的資源損耗。
- 提高了響應(yīng)速度,無(wú)需等待創(chuàng)建和銷毀的時(shí)間,一旦任務(wù)到達(dá)的時(shí)候,即可通過(guò)線程池的線程執(zhí)行。
- 提高了線程的管控性,線程是稀缺的資源,如果無(wú)限創(chuàng)建,不僅會(huì)消耗系統(tǒng)資源,還會(huì)降低系統(tǒng)的穩(wěn)定性,使用線程池可以進(jìn)行統(tǒng)一的分配,調(diào)優(yōu)和監(jiān)控。
2. 你知道有多少種創(chuàng)建線程池的方式
JDK1.5以后提供一個(gè)Executors
工具類 ,里面提供一些靜態(tài)工廠方法,生成一些常用的線程池。
newCachedThreadPool
:創(chuàng)建一個(gè)可緩存的線程池。如果線程池的大小超過(guò)了處理任務(wù)所需要的線程,那么就會(huì)回收部分空閑(60秒不執(zhí)行任務(wù))的線程,當(dāng)任務(wù)數(shù)增加時(shí),此線程池又可以智能的添加新線程來(lái)處理任務(wù)。此線程池不會(huì)對(duì)線程池大小做限制,線程池大小完全依賴于操作系統(tǒng)(或者說(shuō)JVM)能夠創(chuàng)建的最大線程大小。
那我們來(lái)看看底層的方法和實(shí)現(xiàn)過(guò)程:
底層:
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
我們實(shí)現(xiàn)的步驟:
public class ThreadPoolDemo { public static void main(String[] args) { threadPoolTest(); } private static void threadPoolTest() { // 1. 使用工廠類獲取線程池對(duì)象 ExecutorService executorService = Executors.newCachedThreadPool(); // 2. 提交任務(wù) for (int i = 1; i < 8; i++) { executorService.submit(new MyRunnable(i)); } } } // 我們的任務(wù)類 class MyRunnable implements Runnable { private int id; public MyRunnable(int id) { this.id = id; } @Override public void run() { // 打印是哪個(gè)線程的名稱。 System.out.println(Thread.currentThread().getName() + "執(zhí)行了任務(wù)" + id); } }
可以得到的結(jié)果:
可以發(fā)現(xiàn),線程池的開(kāi)啟是一直運(yùn)行的狀態(tài),而如果你想結(jié)束的話,可以使用一個(gè)shutdown方法即 executorService.shutdown();
每次任務(wù)都會(huì)創(chuàng)建多一個(gè)線程出來(lái)了。
我們可以看下newCacheThreadPool的運(yùn)行流程如下:
- 提交任務(wù)進(jìn)線程池。
- 因?yàn)閏orePoolSize為0的關(guān)系,不創(chuàng)建核心線程,線程池最大為Integer.MAX_VALUE。
- 嘗試將任務(wù)添加到SynchronousQueue隊(duì)列。
- 如果SynchronousQueue入列成功,等待被當(dāng)前運(yùn)行的線程空閑后拉取執(zhí)行。如果當(dāng)前沒(méi)有空閑線程,那么就創(chuàng)建一個(gè)非核心線程,然后從SynchronousQueue拉取任務(wù)并在當(dāng)前線程執(zhí)行。
- 如果SynchronousQueue已有任務(wù)在等待,入列操作將會(huì)阻塞。
當(dāng)需要執(zhí)行很多短時(shí)間的任務(wù)時(shí),newCacheThreadPool
的線程復(fù)用率比較高, 會(huì)顯著的提高性能。而且線程60s后會(huì)回收,意味著即使沒(méi)有任務(wù)進(jìn)來(lái),newCacheThreadPool
并不會(huì)占用很多資源。
newFixedThreadPool
:創(chuàng)建固定大小的線程池。每次提交一個(gè)任務(wù)就創(chuàng)建一個(gè)線程,直到線程達(dá)到線程池的最大大小。線程池的大小一旦達(dá)到最大值就會(huì)保持不變,如果某個(gè)線程因?yàn)閳?zhí)行異常而結(jié)束,那么線程池會(huì)補(bǔ)充一個(gè)新線程。
還是一樣看下底層和代碼實(shí)現(xiàn)過(guò)程吧:
底層:
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
代碼實(shí)現(xiàn)過(guò)程:
public static void main(String[] args) { // threadPoolTest(); threadPoolTest2(); } private static void threadPoolTest2() { // 1. 使用工廠類獲取線程池對(duì)象 ExecutorService executorService = Executors.newFixedThreadPool(3); // 2. 提交任務(wù) for (int i = 1; i < 8; i++) { executorService.submit(new MyRunnable(i)); } }
得到的結(jié)果:
pool-1-thread-2執(zhí)行了任務(wù)2
pool-1-thread-1執(zhí)行了任務(wù)1
pool-1-thread-3執(zhí)行了任務(wù)3
pool-1-thread-3執(zhí)行了任務(wù)6
pool-1-thread-1執(zhí)行了任務(wù)5
pool-1-thread-2執(zhí)行了任務(wù)4
pool-1-thread-3執(zhí)行了任務(wù)7
我們可以發(fā)現(xiàn),創(chuàng)建線程數(shù)量就是我們指定3,核心線程數(shù)量和總線程數(shù)量相等,都是傳入的參數(shù)nThreads,所以只能創(chuàng)建核心線程,不能創(chuàng)建非核心線程。因?yàn)?code>LinkedBlockingQueue的默認(rèn)大小是Integer.MAX_VALUE
,故如果核心線程空閑,則交給核心線程處理;如果核心線程不空閑,則入列等待,直到核心線程空閑。
與newCacheThreadPool的區(qū)別:
- 因?yàn)?corePoolSize == maximumPoolSize ,所以FixedThreadPool只會(huì)創(chuàng)建核心線程。 而CachedThreadPool因?yàn)閏orePoolSize=0,所以只會(huì)創(chuàng)建非核心線程。
- 在 getTask() 方法,如果隊(duì)列里沒(méi)有任務(wù)可取,線程會(huì)一直阻塞在 LinkedBlockingQueue.take() ,線程不會(huì)被回收。 CachedThreadPool會(huì)在60s后收回。
- 由于線程不會(huì)被回收,會(huì)一直卡在阻塞,所以沒(méi)有任務(wù)的情況下, FixedThreadPool占用資源更多。
- 都幾乎不會(huì)觸發(fā)拒絕策略,但是原理不同。FixedThreadPool是因?yàn)樽枞?duì)列可以很大(最大為Integer最大值),故幾乎不會(huì)觸發(fā)拒絕策略;CachedThreadPool是因?yàn)榫€程池很大(最大為Integer最大值),幾乎不會(huì)導(dǎo)致線程數(shù)量大于最大線程數(shù),故幾乎不會(huì)觸發(fā)拒絕策略。
newSingleThreadExecutor
:創(chuàng)建一個(gè)單線程的線程池。這個(gè)線程池只有一個(gè)線程在工作,也就是相當(dāng)于單線程串行執(zhí)行所有任務(wù)。如果這個(gè)唯一的線程因?yàn)楫惓=Y(jié)束,那么會(huì)有一個(gè)新的線程來(lái)替代它。此線程池保證所有任務(wù)的執(zhí)行順序按照任務(wù)的提交順序執(zhí)行
還是一樣看下底層和代碼實(shí)現(xiàn)過(guò)程吧:
底層:
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
代碼實(shí)現(xiàn)過(guò)程:
public static void main(String[] args) { // threadPoolTest(); // threadPoolTest2(); threadPoolTest3(); } private static void threadPoolTest3() { // 1. 使用工廠類獲取線程池對(duì)象 ExecutorService executorService = Executors.newSingleThreadExecutor(); // 2. 提交任務(wù) for (int i = 1; i < 8; i++) { executorService.submit(new MyRunnable(i)); } }
得到的結(jié)果:
pool-1-thread-1執(zhí)行了任務(wù)1
pool-1-thread-1執(zhí)行了任務(wù)2
pool-1-thread-1執(zhí)行了任務(wù)3
pool-1-thread-1執(zhí)行了任務(wù)4
pool-1-thread-1執(zhí)行了任務(wù)5
pool-1-thread-1執(zhí)行了任務(wù)6
pool-1-thread-1執(zhí)行了任務(wù)7
可以發(fā)現(xiàn),只創(chuàng)建了一個(gè)線程,有且僅有一個(gè)核心線程( corePoolSize == maximumPoolSize=1
),使用了LinkedBlockingQueue
(容量很大),所以,不會(huì)創(chuàng)建非核心線程。所有任務(wù)按照先來(lái)先執(zhí)行的順序執(zhí)行。如果這個(gè)唯一的線程不空閑,那么新來(lái)的任務(wù)會(huì)存儲(chǔ)在任務(wù)隊(duì)列里等待執(zhí)行。
newScheduledThreadpool
:創(chuàng)建一個(gè)大小無(wú)限的線程池。此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求。
還是一樣看下底層和代碼實(shí)現(xiàn)過(guò)程吧:
底層:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); } public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue()); }
代碼實(shí)現(xiàn):
public static void main(String[] args) { // threadPoolTest(); // threadPoolTest2(); // threadPoolTest3(); threadPoolTest4(); } private static void threadPoolTest4() { // 1. 使用工廠類獲取線程池對(duì)象 ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3); // 2. 每個(gè)任務(wù)延遲兩秒執(zhí)行 for (int i = 1; i < 8; i++) { scheduledExecutorService.schedule(new MyRunnable(i), 2, TimeUnit.SECONDS); } System.out.println("看是不是我先執(zhí)行了!"); }
可以看到的結(jié)果:
看是不是我先執(zhí)行了!
pool-1-thread-1執(zhí)行了任務(wù)1
pool-1-thread-1執(zhí)行了任務(wù)4
pool-1-thread-2執(zhí)行了任務(wù)2
pool-1-thread-3執(zhí)行了任務(wù)3
pool-1-thread-2執(zhí)行了任務(wù)6
pool-1-thread-1執(zhí)行了任務(wù)5
pool-1-thread-3執(zhí)行了任務(wù)7
我們可以發(fā)現(xiàn),線程池只創(chuàng)建我們指定的線程數(shù),并且返回的是一個(gè)繼承了ExecutorService的ScheduledExecutorService
的接口。它給我們提供一些延遲的方法:
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit); 延遲時(shí)間單位是unit,時(shí)間數(shù)是delay,任務(wù)是Runnable類型的command。 public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit); 而這個(gè)方法是上面方法的重載,不一樣的是任務(wù)是Callable類型的
3. 線程池的五種狀態(tài)你有了解嗎?
線程池它有以下五種狀態(tài):
具體有:
RUNNING
:這是最正常的狀態(tài),能正常接受新的任務(wù),正常處理等待隊(duì)列中的任務(wù)。SHUTDOWN
:不接受新的任務(wù)提交,但是會(huì)繼續(xù)處理正在執(zhí)行的業(yè)務(wù)并且也會(huì)處理阻塞隊(duì)列中的任務(wù)。STOP
:不接受新的任務(wù)提交,不再處理等待隊(duì)列中的任務(wù),并且中斷正在執(zhí)行任務(wù)的線程。TIDYING
:所有的任務(wù)都執(zhí)行完畢或銷毀了,當(dāng)前活動(dòng)線程數(shù)為 0,線程池的狀態(tài)在轉(zhuǎn)換為 TIDYING 狀態(tài)時(shí),會(huì)執(zhí)行鉤子方法 terminated()進(jìn)入終止?fàn)顟B(tài)。TERMINATED
:線程池徹底終止,即terminated()方法結(jié)束后,線程池的狀態(tài)就會(huì)變成這個(gè)。
4. 你知道ThreadPoolExecutor的構(gòu)造方法和參數(shù)嗎
我們先來(lái)看看它的構(gòu)造方法有哪些:
// 五個(gè)參數(shù)的構(gòu)造函數(shù) public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {...} // 六個(gè)參數(shù)的構(gòu)造函數(shù) public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {...} // 六個(gè)參數(shù)的構(gòu)造函數(shù) public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {...} // 七個(gè)參數(shù)的構(gòu)造函數(shù) public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {...}
我們?cè)賮?lái)詳解下構(gòu)造方法中涉及的7個(gè)參數(shù),其中最重要5個(gè)參數(shù)就是第一個(gè)構(gòu)造方法中的。
- int corePoolSize:該線程池中核心線程數(shù)量
核心線程:線程池中有兩類線程,核心線程和非核心線程。核心線程默認(rèn)情況下會(huì)一直存在于線程池中,即使這個(gè)核心線程什么都不干,而非核心線程(臨時(shí)工)如果長(zhǎng)時(shí)間的閑置,就會(huì)被銷毀。但是如果將
allowCoreThreadTimeOut
設(shè)置為true時(shí),核心線程也是會(huì)被超時(shí)回收。
- int maximumPoolSize:該線程池中允許存在的工作線程的最大數(shù)量。
該值相當(dāng)于核心線程數(shù)量 + 非核心線程數(shù)量。
- long keepAliveTime:非核心線程閑置超時(shí)時(shí)長(zhǎng)。
非核心線程如果處于閑置狀態(tài)超過(guò)該值,就會(huì)被銷毀。如果設(shè)置allowCoreThreadTimeOut(true),
則會(huì)也作用于核心線程。
- TimeUnit unit:keepAliveTime的時(shí)間單位。
TimeUnit是一個(gè)枚舉類型 ,包括以下屬性:
NANOSECONDS : 1微毫秒
MICROSECONDS : 1微秒
MILLISECONDS : 1毫秒
SECONDS : 秒
MINUTES : 分
HOURS : 小時(shí)
DAYS : 天
- BlockingQueue workQueue:阻塞隊(duì)列,維護(hù)著等待執(zhí)行的Runnable任務(wù)對(duì)象。
當(dāng)新任務(wù)來(lái)的時(shí)候,會(huì)先判斷當(dāng)前運(yùn)行線程數(shù)量是否達(dá)到了核心線程數(shù),如果達(dá)到了,就會(huì)被存放在阻塞隊(duì)列中排隊(duì)等待執(zhí)行。
常用的幾個(gè)阻塞隊(duì)列:
1.ArrayBlockingQueue
數(shù)組阻塞隊(duì)列,底層數(shù)據(jù)結(jié)構(gòu)是數(shù)組,需要指定隊(duì)列的大小。
2.SynchronousQueue
同步隊(duì)列,內(nèi)部容量為0,每個(gè)put操作必須等待一個(gè)take操作,反之亦然。
3.DelayQueue
延遲隊(duì)列,該隊(duì)列中的元素只有當(dāng)其指定的延遲時(shí)間到了,才能夠從隊(duì)列中獲取到該元素 。
4.LinkedBlockingQueue
鏈?zhǔn)阶枞?duì)列,底層數(shù)據(jù)結(jié)構(gòu)是鏈表,默認(rèn)大小是Integer.MAX_VALUE
,也可以指定大小。
還有兩個(gè)非必須的參數(shù):
- ThreadFactory threadFactory
創(chuàng)建線程的工廠 ,用于批量創(chuàng)建線程,統(tǒng)一在創(chuàng)建線程時(shí)設(shè)置一些參數(shù),如是否守護(hù)線程、線程的優(yōu)先級(jí)等。如果不指定,會(huì)新建一個(gè)默認(rèn)的線程工廠。
- RejectedExecutionHandler handler
拒絕處理策略,在線程數(shù)量大于最大線程數(shù)后就會(huì)采用拒絕處理策略,四種拒絕處理的策略為 :
ThreadPoolExecutor.AbortPolicy
:默認(rèn)拒絕處理策略,丟棄任務(wù)并拋出RejectedExecutionException
異常。ThreadPoolExecutor.DiscardPolicy
:丟棄新來(lái)的任務(wù),但是不拋出異常。ThreadPoolExecutor.DiscardOldestPolicy
:丟棄隊(duì)列頭部(最舊的)的任務(wù),然后重新嘗試執(zhí)行程序(如果再次失敗,重復(fù)此過(guò)程)。ThreadPoolExecutor.CallerRunsPolicy
:由調(diào)用線程處理該任務(wù)。
5. 你可以說(shuō)下線程池的執(zhí)行過(guò)程原理嗎
昨天MyGirl跟我講了一下她去銀行辦理業(yè)務(wù)的一個(gè)場(chǎng)景:
- 首先MyGirl(任務(wù)A)先去銀行(線程池)辦理業(yè)務(wù),她發(fā)現(xiàn)她來(lái)早了,現(xiàn)在銀行才剛開(kāi)門(mén),柜臺(tái)窗口服務(wù)員還沒(méi)過(guò)來(lái)(相當(dāng)于線程池中的初始線程為0),此時(shí)銀行經(jīng)理看到MyGirl來(lái)了,就安排她去一號(hào)柜臺(tái)窗口并安排了1號(hào)正式工作人員來(lái)接待她。
- 在MyGirl的業(yè)務(wù)還沒(méi)辦完時(shí),一個(gè)不知名的路人甲(任務(wù)B)出現(xiàn)了,他也是要來(lái)銀行辦業(yè)務(wù),于是銀行經(jīng)理安排他去二號(hào)柜臺(tái)并安排了2號(hào)正式工作人員。假設(shè)該銀行的柜臺(tái)窗口就只有兩個(gè)(核心線程數(shù)量2)。
- 緊接著,在所有人業(yè)務(wù)都還沒(méi)做完的情況,持續(xù)來(lái)個(gè)三個(gè)不知名的路人乙丙丁,他們也是要來(lái)辦業(yè)務(wù)的,但是由于柜臺(tái)滿了,安排了他們?nèi)ヅ赃叺你y行大廳的座位上(阻塞隊(duì)列,這里假設(shè)大小為3)等候并給了對(duì)應(yīng)順序的號(hào)碼,說(shuō)等前面兩個(gè)人辦理完后,按順序叫號(hào)你們呦,請(qǐng)注意聽(tīng)。
- 過(guò)一會(huì),一個(gè)路人戊也想來(lái)銀行辦理業(yè)務(wù),而經(jīng)理看到柜臺(tái)滿了,座位滿了,只能安排了一個(gè)臨時(shí)工(非核心線程,這里假設(shè)最大線程為3,即非核心為1)手持pad設(shè)備并給路人戊去辦理業(yè)務(wù)。
- 而此時(shí),一個(gè)路人戌過(guò)來(lái)辦理業(yè)務(wù),而經(jīng)理看到柜臺(tái)滿了,座位滿了,臨時(shí)工也安排滿了(最大線程數(shù)+阻塞隊(duì)列都滿了),無(wú)奈經(jīng)理只能掏出一本《如何接待超出最大限度的手冊(cè)》,選擇拒接接待路人戌通知他,過(guò)會(huì)再來(lái)吧您嘞,這里已經(jīng)超負(fù)荷啦!
- 最后,相繼所有人的業(yè)務(wù)都辦完了,現(xiàn)在也沒(méi)人再來(lái)辦業(yè)務(wù),并且臨時(shí)工的空閑時(shí)間也超過(guò)了1小時(shí)以上了(最大空閑時(shí)間默認(rèn)60秒),經(jīng)理讓臨時(shí)工都先下班回家了(銷毀線程)。
- 但是一個(gè)銀行要保證正常的運(yùn)行,只能讓正式員工繼續(xù)上班,不得提早下班。
而實(shí)際上線程的流程原理跟這個(gè)一樣,我們來(lái)看下處理任務(wù)的核心方法execute
,它的源碼大概是什么樣子的呢,當(dāng)然我們也可以看源碼中的注釋,里面也寫(xiě)的很清楚。這里具體講下思路。
public void execute(Runnable command) { if (command == null) throw new NullPointerException(); // 1. 獲取ctl,ctl是記錄著線程池狀態(tài)和線程數(shù)。 int c = ctl.get(); // 2. 判斷當(dāng)前線程數(shù)小于corePoolSize核心線程,則調(diào)用addWorker創(chuàng)建核心線程執(zhí)行任務(wù) if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) return; // 創(chuàng)建線程失敗,需要重新獲取clt的狀態(tài)和線程數(shù)。 c = ctl.get(); } // 3. 如果不小于corePoolSize,進(jìn)入下面的方法。 // 判斷線程池是否運(yùn)行狀態(tài)并且運(yùn)行線程數(shù)大于corePoolSize,將任務(wù)添加到workQueue隊(duì)列。 if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); // 3.1 再次檢查線程池是否運(yùn)行狀態(tài)。 // 如果isRunning返回false(狀態(tài)檢查),則remove這個(gè)任務(wù),然后執(zhí)行拒絕策略。 if (! isRunning(recheck) && remove(command)) reject(command); // 3.2 線程池處于running狀態(tài),但是沒(méi)有線程,則創(chuàng)建線程加入到線程池中 else if (workerCountOf(recheck) == 0) addWorker(null, false); } // 4. 如果放入workQueue失敗,則創(chuàng)建非核心線程執(zhí)行任務(wù), // 如果這時(shí)創(chuàng)建非核心線程失敗(當(dāng)前線程總數(shù)不小于maximumPoolSize時(shí)),就會(huì)執(zhí)行拒絕策略。 else if (!addWorker(command, false)) reject(command); }
我們可以大概看下思路圖:
先解釋下ctl
變量ctl定義為AtomicInteger,記錄了“線程池中的任務(wù)數(shù)量”和“線程池的狀態(tài)”兩個(gè)信息。以高三位記錄著線程池的狀態(tài)和低29位記錄線程池中的任務(wù)數(shù)量。
RUNNING : 111
SHUTDOWN : 000
STOP : 001
TIDYING : 010
TERMINATED : 011
最后總結(jié)一下執(zhí)行過(guò)程:
- 任務(wù)到達(dá)時(shí),會(huì)先判斷核心線程是否滿了,不滿則調(diào)用addWorker方法創(chuàng)建核心線程執(zhí)行任務(wù)。
- 然后會(huì)判斷下線程池中的線程數(shù) < 核心線程,無(wú)論線程是否空閑,都會(huì)新建一個(gè)核心線程執(zhí)行任務(wù)(讓核心線程數(shù)量快速達(dá)到核心線程總數(shù))。此步驟會(huì)開(kāi)啟鎖
mainLock.lock();。
- 而在線程池中的線程數(shù) >= 核心線程時(shí),新來(lái)的線程任務(wù)會(huì)進(jìn)入任務(wù)阻塞隊(duì)列中等待,然后空閑的核心線程會(huì)依次去阻塞隊(duì)列中取任務(wù)來(lái)執(zhí)行。
- 當(dāng)阻塞隊(duì)列滿了,說(shuō)明這個(gè)時(shí)候任務(wù)很多了,此時(shí)就需要一些非核心線程臨時(shí)工來(lái)執(zhí)行這些任務(wù)了。于是會(huì)創(chuàng)建非核心線程去執(zhí)行這個(gè)任務(wù)。
- 最后當(dāng)阻塞隊(duì)列滿了, 且總線程數(shù)達(dá)到了maximumPoolSize,則會(huì)采取拒絕策略進(jìn)行處理。
- 當(dāng)非核心線程取任務(wù)的時(shí)間達(dá)到keepAliveTime還沒(méi)有取到任務(wù)即空閑時(shí)間,就會(huì)回收非核心線程。
6. 能否寫(xiě)一個(gè)簡(jiǎn)單線程池的demo?
這里還是直接用簡(jiǎn)單的ThreadPoolExecutor
創(chuàng)建吧,等后續(xù)寫(xiě)線程池相關(guān)文章,再詳細(xì)寫(xiě)自己創(chuàng)建的線程池吧。
我們先創(chuàng)建一個(gè)任務(wù)類Task:
/** * 自定義任務(wù)類 */ public class Task implements Runnable{ private int id; public Task(int id) { this.id = id; } @Override public void run() { System.out.println(Thread.currentThread().getName() + "即將執(zhí)行的任務(wù)是" + id + "任務(wù)"); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "執(zhí)行完成的任務(wù)是" + id + "任務(wù)"); } }
測(cè)試代碼:
public class ThreadPoolExecutorDemo { private static final int CORE_POOL_SIZE = 3; private static final int MAX_POOL_SIZE = 5; private static final int QUEUE_CAPACITY = 10; private static final Long KEEP_ALIVE_TIME = 1l; public static void main(String[] args) { //通過(guò)ThreadPoolExecutor構(gòu)造函數(shù)自定義參數(shù)創(chuàng)建 ThreadPoolExecutor executor = new ThreadPoolExecutor( CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS, new ArrayBlockingQueue<>(QUEUE_CAPACITY), new ThreadPoolExecutor.CallerRunsPolicy()); for (int i = 0; i < 10; i++) { Task task = new Task( i); //執(zhí)行Runnable executor.execute(task); } //終止線程池 executor.shutdown(); while (!executor.isTerminated()) { } System.out.println("線程已經(jīng)全部執(zhí)行完"); } }
得到的結(jié)果:
pool-1-thread-1即將執(zhí)行的任務(wù)是0任務(wù)
pool-1-thread-3即將執(zhí)行的任務(wù)是2任務(wù)
pool-1-thread-2即將執(zhí)行的任務(wù)是1任務(wù)
pool-1-thread-1執(zhí)行完成的任務(wù)是0任務(wù)
pool-1-thread-3執(zhí)行完成的任務(wù)是2任務(wù)
pool-1-thread-1即將執(zhí)行的任務(wù)是3任務(wù)
pool-1-thread-3即將執(zhí)行的任務(wù)是4任務(wù)
pool-1-thread-2執(zhí)行完成的任務(wù)是1任務(wù)
pool-1-thread-2即將執(zhí)行的任務(wù)是5任務(wù)
pool-1-thread-3執(zhí)行完成的任務(wù)是4任務(wù)
pool-1-thread-1執(zhí)行完成的任務(wù)是3任務(wù)
pool-1-thread-3即將執(zhí)行的任務(wù)是6任務(wù)
pool-1-thread-1即將執(zhí)行的任務(wù)是7任務(wù)
pool-1-thread-2執(zhí)行完成的任務(wù)是5任務(wù)
pool-1-thread-2即將執(zhí)行的任務(wù)是8任務(wù)
pool-1-thread-3執(zhí)行完成的任務(wù)是6任務(wù)
pool-1-thread-1執(zhí)行完成的任務(wù)是7任務(wù)
pool-1-thread-3即將執(zhí)行的任務(wù)是9任務(wù)
pool-1-thread-2執(zhí)行完成的任務(wù)是8任務(wù)
pool-1-thread-3執(zhí)行完成的任務(wù)是9任務(wù)
線程已經(jīng)全部執(zhí)行完
總結(jié)
這篇文章就到這里了,如果這篇文章對(duì)你也有所幫助,希望您能多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
詳解spring boot容器加載完后執(zhí)行特定操作
這篇文章主要介紹了詳解spring boot容器加載完后執(zhí)行特定操作,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-01-01Java對(duì)象轉(zhuǎn)Json,關(guān)于@JSONField對(duì)象字段重命名和順序問(wèn)題
這篇文章主要介紹了Java對(duì)象轉(zhuǎn)Json,關(guān)于@JSONField對(duì)象字段重命名和順序問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-08-08SpringMVC中RequestMapping注解(作用、出現(xiàn)的位置、屬性)
這篇文章主要介紹了SpringMVC中RequestMapping注解(作用、出現(xiàn)的位置、屬性),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01基于SpringCloudAlibaba+Skywalking的全鏈路監(jiān)控設(shè)計(jì)方案
這篇文章主要介紹了基于SpringCloudAlibaba+Skywalking的全鏈路監(jiān)控設(shè)計(jì)方案,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2024-01-01這么優(yōu)雅的Java ORM沒(méi)見(jiàn)過(guò)吧!
這篇文章主要介紹了Java ORM的相關(guān)資料,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2021-01-01Java項(xiàng)目實(shí)戰(zhàn)之在線考試系統(tǒng)的實(shí)現(xiàn)(系統(tǒng)介紹)
這篇文章主要介紹了Java項(xiàng)目實(shí)戰(zhàn)之在線考試系統(tǒng)的實(shí)現(xiàn)(系統(tǒng)介紹),本文通過(guò)實(shí)例代碼,截圖的形式給大家展示系統(tǒng)技術(shù)架構(gòu),需要的朋友可以參考下2020-02-02