一文帶你弄懂Java中線程池的原理
在工作中,我們經(jīng)常使用線程池,但是你真的了解線程池的原理嗎?同時(shí),線程池工作原理和底層實(shí)現(xiàn)原理也是面試經(jīng)常問(wèn)的考題,所以,今天我們一起聊聊線程池的原理吧。
為什么要用線程池
使用線程池主要有以下三個(gè)原因:
- 降低資源消耗。通過(guò)重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷(xiāo)毀造成的消耗。
- 提升響應(yīng)速度。當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不需要等到線程創(chuàng)建就能立即執(zhí)行。
- 可以對(duì)線程做統(tǒng)一管理。線程是稀缺資源,如果無(wú)限制地創(chuàng)建,不僅會(huì)消耗系統(tǒng)資源,還會(huì)降低系統(tǒng)穩(wěn)定性,使用線程池可以進(jìn)行統(tǒng)一分配、調(diào)優(yōu)和監(jiān)控。
線程池的原理
Java中的線程池頂層接口是Executor
接口,ThreadPoolExecutor
是這個(gè)接口的實(shí)現(xiàn)類(lèi)。
我們先看看ThreadPoolExecutor
類(lèi)。
ThreadPoolExecutor提供的構(gòu)造方法
// 七個(gè)參數(shù)的構(gòu)造函數(shù) public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
我們先看看這些參數(shù)是什么意思:
int corePoolSize:該線程池中核心線程數(shù)最大值
核心線程:線程池中有兩類(lèi)線程,核心線程和非核心線程。核心線程默認(rèn)情況下會(huì)一直存在于線程池中,即使這個(gè)核心線程什么都不干(鐵飯碗),而非核心線程如果長(zhǎng)時(shí)間的閑置,就會(huì)被銷(xiāo)毀(臨時(shí)工)。
int maximumPoolSize:該線程池中線程總數(shù)最大值 。
該值等于核心線程數(shù)量 + 非核心線程數(shù)量。
long keepAliveTime:非核心線程閑置超時(shí)時(shí)長(zhǎng)。
非核心線程如果處于閑置狀態(tài)超過(guò)該值,就會(huì)被銷(xiāo)毀。如果設(shè)置allowCoreThreadTimeOut(true),則會(huì)也作用于核心線程。
TimeUnit unit:keepAliveTime的單位。
TimeUnit是一個(gè)枚舉類(lèi)型。
BlockingQueue workQueue:阻塞隊(duì)列,維護(hù)著等待執(zhí)行的Runnable任務(wù)對(duì)象。
常用的幾個(gè)阻塞隊(duì)列:
- LinkedBlockingQueue:鏈?zhǔn)阶枞?duì)列,底層數(shù)據(jù)結(jié)構(gòu)是鏈表,默認(rèn)大小是
Integer.MAX_VALUE
,也可以指定大小。 - ArrayBlockingQueue:數(shù)組阻塞隊(duì)列,底層數(shù)據(jù)結(jié)構(gòu)是數(shù)組,需要指定隊(duì)列的大小。
- SynchronousQueue:同步隊(duì)列,內(nèi)部容量為0,每個(gè)put操作必須等待一個(gè)take操作,反之亦然。
- DelayQueue:延遲隊(duì)列,該隊(duì)列中的元素只有當(dāng)其指定的延遲時(shí)間到了,才能夠從隊(duì)列中獲取到該元素 。
ThreadFactory threadFactory
創(chuàng)建線程的工廠 ,用于批量創(chuàng)建線程,統(tǒng)一在創(chuàng)建線程時(shí)設(shè)置一些參數(shù),如是否守護(hù)線程、線程的優(yōu)先級(jí)等。如果不指定,會(huì)新建一個(gè)默認(rèn)的線程工廠。
static class DefaultThreadFactory implements ThreadFactory { // 省略屬性 // 構(gòu)造函數(shù) DefaultThreadFactory() { SecurityManager s = System.getSecurityManager(); group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-"; } // 省略 }
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ù)。
ThreadPoolExecutor的策略
線程池本身有一個(gè)調(diào)度線程,這個(gè)線程就是用于管理布控整個(gè)線程池里的各種任務(wù)和事務(wù),例如創(chuàng)建線程、銷(xiāo)毀線程、任務(wù)隊(duì)列管理、線程隊(duì)列管理等等。
故線程池也有自己的狀態(tài)。ThreadPoolExecutor
類(lèi)中使用了一些final int
常量變量來(lái)表示線程池的狀態(tài) ,分別為RUNNING、SHUTDOWN、STOP、TIDYING 、TERMINATED。
// runState is stored in the high-order bits private static final int RUNNING = -1 << COUNT_BITS; private static final int SHUTDOWN = 0 << COUNT_BITS; private static final int STOP = 1 << COUNT_BITS; private static final int TIDYING = 2 << COUNT_BITS; private static final int TERMINATED = 3 << COUNT_BITS;
- 線程池創(chuàng)建后處于RUNNING狀態(tài)。
- 調(diào)用shutdown()方法后處于SHUTDOWN狀態(tài),線程池不能接受新的任務(wù),清除一些空閑worker,不會(huì)等待阻塞隊(duì)列的任務(wù)完成。
- 調(diào)用shutdownNow()方法后處于STOP狀態(tài),線程池不能接受新的任務(wù),中斷所有線程,阻塞隊(duì)列中沒(méi)有被執(zhí)行的任務(wù)全部丟棄。此時(shí),poolsize=0,阻塞隊(duì)列的size也為0。
- 當(dāng)所有的任務(wù)已終止,ctl記錄的”任務(wù)數(shù)量”為0,線程池會(huì)變?yōu)?strong>TIDYING狀態(tài)。接著會(huì)執(zhí)行terminated()函數(shù)。
- 線程池處在TIDYING狀態(tài)時(shí),執(zhí)行完terminated()方法之后,就會(huì)由 TIDYING -> TERMINATED, 線程池被設(shè)置為T(mén)ERMINATED狀態(tài)。
線程池主要的任務(wù)處理流程
處理任務(wù)的核心方法是execute
,我們看看 JDK 1.8 源碼中ThreadPoolExecutor
是如何處理線程任務(wù)的:
// JDK 1.8 public void execute(Runnable command) { if (command == null) throw new NullPointerException(); int c = ctl.get(); // 1.當(dāng)前線程數(shù)小于corePoolSize,則調(diào)用addWorker創(chuàng)建核心線程執(zhí)行任務(wù) if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) return; c = ctl.get(); } // 2.如果不小于corePoolSize,則將任務(wù)添加到workQueue隊(duì)列。 if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); // 2.1 如果isRunning返回false(狀態(tài)檢查),則remove這個(gè)任務(wù),然后執(zhí)行拒絕策略。 if (! isRunning(recheck) && remove(command)) reject(command); // 2.2 線程池處于running狀態(tài),但是沒(méi)有線程,則創(chuàng)建線程 else if (workerCountOf(recheck) == 0) addWorker(null, false); } // 3.如果放入workQueue失敗,則創(chuàng)建非核心線程執(zhí)行任務(wù), // 如果這時(shí)創(chuàng)建非核心線程失敗(當(dāng)前線程總數(shù)不小于maximumPoolSize時(shí)),就會(huì)執(zhí)行拒絕策略。 else if (!addWorker(command, false)) reject(command); }
ctl.get()
是獲取線程池狀態(tài),用int
類(lèi)型表示。第二步中,入隊(duì)前進(jìn)行了一次isRunning
判斷,入隊(duì)之后,又進(jìn)行了一次isRunning
判斷。
為什么要二次檢查線程池的狀態(tài)?
在多線程的環(huán)境下,線程池的狀態(tài)是時(shí)刻發(fā)生變化的。很有可能剛獲取線程池狀態(tài)后線程池狀態(tài)就改變了。判斷是否將command
加入workqueue
是線程池之前的狀態(tài)。倘若沒(méi)有二次檢查,萬(wàn)一線程池處于非RUNNING狀態(tài)(在多線程環(huán)境下很有可能發(fā)生),那么command
永遠(yuǎn)不會(huì)執(zhí)行。
總結(jié)一下處理流程
- 線程總數(shù)量 < corePoolSize,無(wú)論線程是否空閑,都會(huì)新建一個(gè)核心線程執(zhí)行任務(wù)(讓核心線程數(shù)量快速達(dá)到corePoolSize,在核心線程數(shù)量 < corePoolSize時(shí))。注意,這一步需要獲得全局鎖。
- 線程總數(shù)量 >= corePoolSize時(shí),新來(lái)的線程任務(wù)會(huì)進(jìn)入任務(wù)隊(duì)列中等待,然后空閑的核心線程會(huì)依次去緩存隊(duì)列中取任務(wù)來(lái)執(zhí)行(體現(xiàn)了線程復(fù)用)。
- 當(dāng)緩存隊(duì)列滿(mǎn)了,說(shuō)明這個(gè)時(shí)候任務(wù)已經(jīng)多到爆棚,需要一些“臨時(shí)工”來(lái)執(zhí)行這些任務(wù)了。于是會(huì)創(chuàng)建非核心線程去執(zhí)行這個(gè)任務(wù)。注意,這一步需要獲得全局鎖。
- 緩存隊(duì)列滿(mǎn)了, 且總線程數(shù)達(dá)到了maximumPoolSize,則會(huì)采取上面提到的拒絕策略進(jìn)行處理。
整個(gè)過(guò)程如圖所示:
ThreadPoolExecutor如何做到線程復(fù)用的
我們知道,一個(gè)線程在創(chuàng)建的時(shí)候會(huì)指定一個(gè)線程任務(wù),當(dāng)執(zhí)行完這個(gè)線程任務(wù)之后,線程自動(dòng)銷(xiāo)毀。但是線程池卻可以復(fù)用線程,即一個(gè)線程執(zhí)行完線程任務(wù)后不銷(xiāo)毀,繼續(xù)執(zhí)行另外的線程任務(wù)。那么,線程池如何做到線程復(fù)用呢?
原來(lái),ThreadPoolExecutor在創(chuàng)建線程時(shí),會(huì)將線程封裝成工作線程worker,并放入工作線程組中,然后這個(gè)worker反復(fù)從阻塞隊(duì)列中拿任務(wù)去執(zhí)行。
這里的addWorker
方法是在上面提到的execute
方法里面調(diào)用的,先看看上半部分:
// ThreadPoolExecutor.addWorker方法源碼上半部分 private boolean addWorker(Runnable firstTask, boolean core) { retry: for (;;) { int c = ctl.get(); int rs = runStateOf(c); // Check if queue empty only if necessary. if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())) return false; for (;;) { int wc = workerCountOf(c); if (wc >= CAPACITY || // 1.如果core是ture,證明需要?jiǎng)?chuàng)建的線程為核心線程,則先判斷當(dāng)前線程是否大于核心線程 // 如果core是false,證明需要?jiǎng)?chuàng)建的是非核心線程,則先判斷當(dāng)前線程數(shù)是否大于總線程數(shù) // 如果不小于,則返回false wc >= (core ? corePoolSize : maximumPoolSize)) return false; if (compareAndIncrementWorkerCount(c)) break retry; c = ctl.get(); // Re-read ctl if (runStateOf(c) != rs) continue retry; // else CAS failed due to workerCount change; retry inner loop } }
上半部分主要是判斷線程數(shù)量是否超出閾值,超過(guò)了就返回false。我們繼續(xù)看下半部分:
// ThreadPoolExecutor.addWorker方法源碼下半部分 boolean workerStarted = false; boolean workerAdded = false; Worker w = null; try { // 1.創(chuàng)建一個(gè)worker對(duì)象 w = new Worker(firstTask); // 2.實(shí)例化一個(gè)Thread對(duì)象 final Thread t = w.thread; if (t != null) { // 3.線程池全局鎖 final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { // Recheck while holding lock. // Back out on ThreadFactory failure or if // shut down before lock acquired. int rs = runStateOf(ctl.get()); if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) { if (t.isAlive()) // precheck that t is startable throw new IllegalThreadStateException(); workers.add(w); int s = workers.size(); if (s > largestPoolSize) largestPoolSize = s; workerAdded = true; } } finally { mainLock.unlock(); } if (workerAdded) { // 4.啟動(dòng)這個(gè)線程 t.start(); workerStarted = true; } } } finally { if (! workerStarted) addWorkerFailed(w); } return workerStarted; }
創(chuàng)建worker
對(duì)象,并初始化一個(gè)Thread
對(duì)象,然后啟動(dòng)這個(gè)線程對(duì)象。
我們接著看看Worker
類(lèi),僅展示部分源碼:
// Worker類(lèi)部分源碼 private final class Worker extends AbstractQueuedSynchronizer implements Runnable{ final Thread thread; Runnable firstTask; Worker(Runnable firstTask) { setState(-1); // inhibit interrupts until runWorker this.firstTask = firstTask; this.thread = getThreadFactory().newThread(this); } public void run() { runWorker(this); } //其余代碼略... }
Worker
類(lèi)實(shí)現(xiàn)了Runnable
接口,所以Worker
也是一個(gè)線程任務(wù)。在構(gòu)造方法中,創(chuàng)建了一個(gè)線程,線程的任務(wù)就是自己。故addWorker
方法調(diào)用addWorker方法源碼下半部分中的第4步t.start
,會(huì)觸發(fā)Worker
類(lèi)的run
方法被JVM調(diào)用。
我們?cè)倏纯?code>runWorker的邏輯:
// Worker.runWorker方法源代碼 final void runWorker(Worker w) { Thread wt = Thread.currentThread(); Runnable task = w.firstTask; w.firstTask = null; // 1.線程啟動(dòng)之后,通過(guò)unlock方法釋放鎖 w.unlock(); // allow interrupts boolean completedAbruptly = true; try { // 2.Worker執(zhí)行firstTask或從workQueue中獲取任務(wù),如果getTask方法不返回null,循環(huán)不退出 while (task != null || (task = getTask()) != null) { // 2.1進(jìn)行加鎖操作,保證thread不被其他線程中斷(除非線程池被中斷) w.lock(); // If pool is stopping, ensure thread is interrupted; // if not, ensure thread is not interrupted. This // requires a recheck in second case to deal with // shutdownNow race while clearing interrupt // 2.2檢查線程池狀態(tài),倘若線程池處于中斷狀態(tài),當(dāng)前線程將中斷。 if ((runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) && !wt.isInterrupted()) wt.interrupt(); try { // 2.3執(zhí)行beforeExecute beforeExecute(wt, task); Throwable thrown = null; try { // 2.4執(zhí)行任務(wù) task.run(); } catch (RuntimeException x) { thrown = x; throw x; } catch (Error x) { thrown = x; throw x; } catch (Throwable x) { thrown = x; throw new Error(x); } finally { // 2.5執(zhí)行afterExecute方法 afterExecute(task, thrown); } } finally { task = null; w.completedTasks++; // 2.6解鎖操作 w.unlock(); } } completedAbruptly = false; } finally { processWorkerExit(w, completedAbruptly); } }
首先去執(zhí)行創(chuàng)建這個(gè)worker時(shí)就有的任務(wù),當(dāng)執(zhí)行完這個(gè)任務(wù)后,worker的生命周期并沒(méi)有結(jié)束,在while
循環(huán)中,worker會(huì)不斷地調(diào)用getTask
方法從阻塞隊(duì)列中獲取任務(wù)然后調(diào)用task.run()
執(zhí)行任務(wù),從而達(dá)到復(fù)用線程的目的。只要getTask
方法不返回null
,此線程就不會(huì)退出。
當(dāng)然,核心線程池中創(chuàng)建的線程想要拿到阻塞隊(duì)列中的任務(wù),先要判斷線程池的狀態(tài),如果STOP或者TERMINATED,返回null
。
最后看看getTask
方法的實(shí)現(xiàn):
// Worker.getTask方法源碼 private Runnable getTask() { boolean timedOut = false; // Did the last poll() time out? for (;;) { int c = ctl.get(); int rs = runStateOf(c); // Check if queue empty only if necessary. if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { decrementWorkerCount(); return null; } int wc = workerCountOf(c); // Are workers subject to culling? // 1.allowCoreThreadTimeOut變量默認(rèn)是false,核心線程即使空閑也不會(huì)被銷(xiāo)毀 // 如果為true,核心線程在keepAliveTime內(nèi)仍空閑則會(huì)被銷(xiāo)毀。 boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; // 2.如果運(yùn)行線程數(shù)超過(guò)了最大線程數(shù),但是緩存隊(duì)列已經(jīng)空了,這時(shí)遞減worker數(shù)量。 // 如果有設(shè)置允許線程超時(shí)或者線程數(shù)量超過(guò)了核心線程數(shù)量, // 并且線程在規(guī)定時(shí)間內(nèi)均未poll到任務(wù)且隊(duì)列為空則遞減worker數(shù)量 if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) { if (compareAndDecrementWorkerCount(c)) return null; continue; } try { // 3.如果timed為true(想想哪些情況下timed為true),則會(huì)調(diào)用workQueue的poll方法獲取任務(wù). // 超時(shí)時(shí)間是keepAliveTime。如果超過(guò)keepAliveTime時(shí)長(zhǎng), // poll返回了null,上邊提到的while循序就會(huì)退出,線程也就執(zhí)行完了。 // 如果timed為false(allowCoreThreadTimeOut為false // 且wc > corePoolSize為false),則會(huì)調(diào)用workQueue的take方法阻塞在當(dāng)前。 // 隊(duì)列中有任務(wù)加入時(shí),線程被喚醒,take方法返回任務(wù),并執(zhí)行。 Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); if (r != null) return r; timedOut = true; } catch (InterruptedException retry) { timedOut = false; } } }
核心線程的會(huì)一直卡在workQueue.take
方法,被阻塞并掛起,不會(huì)占用CPU資源,直到拿到Runnable
然后返回(當(dāng)然如果allowCoreThreadTimeOut設(shè)置為true
,那么核心線程就會(huì)去調(diào)用poll
方法,因?yàn)?code>poll可能會(huì)返回null
,所以這時(shí)候核心線程滿(mǎn)足超時(shí)條件也會(huì)被銷(xiāo)毀)。
非核心線程會(huì)workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) ,如果超時(shí)還沒(méi)有拿到,下一次循環(huán)判斷compareAndDecrementWorkerCount就會(huì)返回null
,Worker對(duì)象的run()
方法循環(huán)體的判斷為null
,任務(wù)結(jié)束,然后線程被系統(tǒng)回收 。
四種常見(jiàn)的線程池
Executors
類(lèi)中提供的幾個(gè)靜態(tài)方法來(lái)創(chuàng)建線程池。
newCachedThreadPool
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
CacheThreadPool
的運(yùn)行流程如下:
- 提交任務(wù)進(jìn)線程池。
- 因?yàn)?strong>corePoolSize為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í),CacheThreadPool的線程復(fù)用率比較高, 會(huì)顯著的提高性能。而且線程60s后會(huì)回收,意味著即使沒(méi)有任務(wù)進(jìn)來(lái),CacheThreadPool并不會(huì)占用很多資源。
newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
核心線程數(shù)量和總線程數(shù)量相等,都是傳入的參數(shù)nThreads,所以只能創(chuàng)建核心線程,不能創(chuàng)建非核心線程。因?yàn)長(zhǎng)inkedBlockingQueue的默認(rèn)大小是Integer.MAX_VALUE,故如果核心線程空閑,則交給核心線程處理;如果核心線程不空閑,則入列等待,直到核心線程空閑。
與CachedThreadPool的區(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
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
有且僅有一個(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è)定長(zhǎng)線程池,支持定時(shí)及周期性任務(wù)執(zhí)行。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); } //ScheduledThreadPoolExecutor(): public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS, new DelayedWorkQueue()); }
四種常見(jiàn)的線程池基本夠我們使用了,但是《阿里巴巴開(kāi)發(fā)手冊(cè)》不建議我們直接使用Executors類(lèi)中的線程池,而是通過(guò)ThreadPoolExecutor
的方式,這樣的處理方式讓寫(xiě)的同學(xué)需要更加明確線程池的運(yùn)行規(guī)則,規(guī)避資源耗盡的風(fēng)險(xiǎn)。
但如果你及團(tuán)隊(duì)本身對(duì)線程池非常熟悉,又確定業(yè)務(wù)規(guī)模不會(huì)大到資源耗盡的程度(比如線程數(shù)量或任務(wù)隊(duì)列長(zhǎng)度可能達(dá)到Integer.MAX_VALUE)時(shí),其實(shí)是可以使用JDK提供的這幾個(gè)接口的,它能讓我們的代碼具有更強(qiáng)的可讀性。
小結(jié)
在工作中,很多人因?yàn)椴涣私饩€程池的實(shí)現(xiàn)原理,把線程池配置錯(cuò)誤,從而導(dǎo)致各種問(wèn)題。希望你們閱讀完本文,能夠?qū)W會(huì)合理的使用線程池。
對(duì)于真正想弄懂java并發(fā)編程的小伙伴,網(wǎng)上的文章還有視頻缺乏系統(tǒng)性,我建議大家還是買(mǎi)點(diǎn)書(shū)籍看看,我推薦兩本我看過(guò)的書(shū)。
《Java并發(fā)編程實(shí)戰(zhàn)》:這本書(shū)深入淺出地介紹了Java線程和并發(fā),是一本非常棒的Java并發(fā)參考手冊(cè)。
《Java并發(fā)編程藝術(shù)》:Java并發(fā)編程的概念本來(lái)就比較復(fù)雜,我們需要的是一本能夠把原理解釋清楚的書(shū)籍,而這本《Java并發(fā)編程的藝術(shù)》書(shū)是國(guó)內(nèi)作者寫(xiě)的Java并發(fā)書(shū)籍,剛好就比上面那一本更簡(jiǎn)單易懂,至少我自己看下來(lái)是這樣的感覺(jué)。
以上就是一文帶你弄懂Java中線程池的原理的詳細(xì)內(nèi)容,更多關(guān)于Java線程池原理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java手寫(xiě)簡(jiǎn)易版HashMap的使用(存儲(chǔ)+查找)
這篇文章主要介紹了Java手寫(xiě)簡(jiǎn)易版HashMap的使用(存儲(chǔ)+查找),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01SpringBoot中連接多個(gè)RabbitMQ的方法詳解
這篇文章主要介紹了SpringBoot中連接多個(gè)RabbitMQ的方法詳解,要實(shí)現(xiàn) SpringBoot 連接多個(gè) RabbitMQ,只能自定義重寫(xiě)一些東西,分別配置才可以,下面一起來(lái)走一下試試,需要的朋友可以參考下2023-10-10IDEA2023常用配置指南(JDK/系統(tǒng)設(shè)置等常用配置)
idea很強(qiáng)大,但是初次安裝默認(rèn)的有很多設(shè)置并不是滿(mǎn)足我們開(kāi)發(fā)的需要,下面這篇文章主要給大家介紹了關(guān)于IDEA2023常用配置(JDK/系統(tǒng)設(shè)置等常用配置)的相關(guān)資料,需要的朋友可以參考下2023-12-12Java?restTemplate發(fā)送get請(qǐng)求query參數(shù)傳遞問(wèn)題解決
這篇文章主要為大家介紹了Java?restTemplate發(fā)送get請(qǐng)求query參數(shù)傳遞問(wèn)題解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11使用SpringCache進(jìn)行緩存數(shù)據(jù)庫(kù)查詢(xún)方式
這篇文章主要介紹了使用SpringCache進(jìn)行緩存數(shù)據(jù)庫(kù)查詢(xún)方式,具有很好的參考價(jià)值,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10Spring應(yīng)用中使用acutator/refresh刷新屬性不生效的問(wèn)題分析及解決
在Spring應(yīng)用收到/actuator/refresh的POST請(qǐng)求后,標(biāo)注了@RefreshScope以及@ConfiguratioinProperties的bean會(huì)被Spring容器重新加載,但是,在實(shí)際應(yīng)用中,并沒(méi)有按照預(yù)期被Spring容器加載,本文將討論導(dǎo)致這種未按預(yù)期刷新的一種原因,感興趣的朋友可以參考下2024-01-01