Java多線程中的Executor框架解析
前言
Executor 框架是 Java5 之后引進(jìn)的,在 Java 5 之后,通過(guò) Executor 來(lái)啟動(dòng)線程比使用 Thread 的 start 方法更好,除了更易管理,效率更好(用線程池實(shí)現(xiàn),節(jié)約開(kāi)銷)。
Executor 框架不僅包括了線程池的管理,還提供了線程工廠、隊(duì)列以及拒絕策略等,Executor 框架讓并發(fā)編程變得更加簡(jiǎn)單。
Executor框架的組成:
- 任務(wù)(Runnable/Callable):任務(wù)通過(guò)Runnable接口或Callable接口進(jìn)行定義。Runnable接口或Callable接口實(shí)現(xiàn)類都可以被 ThreadPoolExecutor執(zhí)行
- 任務(wù)的執(zhí)行(Executor):任務(wù)執(zhí)行機(jī)制的核心接口 Executor,以及繼承自Executor接口的ExecutorService接口。ThreadPoolExecutor實(shí)現(xiàn)了ExecutorService接口
- 異步的計(jì)算結(jié)果(Future):Future接口以及Future接口的實(shí)現(xiàn)類FutureTask類都可以代表異步計(jì)算的結(jié)果。當(dāng)我們把Runnable接口或Callable接口的實(shí)現(xiàn)類提交給ThreadPoolExecutor執(zhí)行后就會(huì)返回一個(gè)Future對(duì)象
一、Executor接口
線程池簡(jiǎn)化了線程的管理工作, 并且JUC提供了一種靈活的線程池實(shí)現(xiàn)來(lái)作為Executor框架的一部分。
在Java類庫(kù)中,任務(wù)執(zhí)行的主要抽象不是Thread而是Executor,Executor只定義了execute一個(gè)方法,是最頂層的接口。
/** * Executes the given command at some time in the future. The command * may execute in a new thread, in a pooled thread, or in the calling * thread, at the discretion of the {@code Executor} implementation. * * @param command the runnable task * @throws RejectedExecutionException if this task cannot be * accepted for execution * @throws NullPointerException if command is null */ void execute(Runnable command);
execute方法接受一個(gè)Runnable參數(shù),這個(gè)方法定義為在未來(lái)的某個(gè)時(shí)間執(zhí)行傳入的方法,方法的運(yùn)行可以在一個(gè)新的線程,在線程池或者在調(diào)用的線程中,該方法無(wú)法接收到線程的執(zhí)行結(jié)果(當(dāng)然Runnable接口本身也沒(méi)有返回值)。
雖然Executor是個(gè)簡(jiǎn)單的接口,但它卻為靈活且強(qiáng)大的異步任務(wù)執(zhí)行框架提供了基礎(chǔ),該框架能支持多種不同類型的任務(wù)執(zhí)行策略。它提供了一種標(biāo)準(zhǔn)的方法將任務(wù)的提交過(guò)程與執(zhí)行過(guò)程解耦開(kāi)來(lái),并用Runnable來(lái)表示任務(wù)。
Executor的實(shí)現(xiàn)還提供了對(duì)生命周期的支持,以及統(tǒng)計(jì)信息收集、應(yīng)用程序管理機(jī)制和性能監(jiān)視等機(jī)制。
**Executor基于生產(chǎn)者-消費(fèi)者模式,提交任務(wù)的操作相當(dāng)于生產(chǎn)者,執(zhí)行任務(wù)的線程相當(dāng)于消費(fèi)者。**如果要在程序中實(shí)現(xiàn)一個(gè)生產(chǎn)者-消費(fèi)者的設(shè)計(jì),那么最簡(jiǎn)單的方式就是使用Executor。
二、ExecutorService接口
Executor框架使用Runnable作為其基本的任務(wù)表示形式。Runnable是一種有很大局限的抽象,它的run方法執(zhí)行任務(wù)后不能返回一個(gè)值或者拋出一個(gè)受檢查的異常。許多任務(wù)實(shí)際上都是存在延遲的計(jì)算——執(zhí)行數(shù)據(jù)庫(kù)查詢,從網(wǎng)絡(luò)上獲取資源,或者計(jì)算某個(gè)復(fù)雜的功能。對(duì)于這些任務(wù),Callable是一種更好的抽象,它認(rèn)為主入口點(diǎn)(call)將返回一個(gè)值,并可能拋出一個(gè)異常。
因此引入了ExecutorService,它繼承自Executor,并且在其基礎(chǔ)上增加了很多功能,可以生成用于跟蹤一個(gè)或多個(gè)異步任務(wù)進(jìn)度的Future的方法,以解決Executor的局限性。
Future表示一個(gè)任務(wù)的生命周期,并且提供了響應(yīng)的方法來(lái)判斷是否已經(jīng)完成或取消,以及獲取任務(wù)的結(jié)果和取消任務(wù)等。
在Future規(guī)范中包含的隱含意義是任務(wù)的生命周期只能前進(jìn),不能后退,就像ExecutorService的生命周期一樣。當(dāng)某個(gè)任務(wù)完成后,它就永遠(yuǎn)停留在完成狀態(tài)。
get方法的行為取決于任務(wù)的狀態(tài)(尚未開(kāi)始、正在運(yùn)行、已完成)。如果任務(wù)已完成那么get會(huì)立即返回或者拋出一個(gè)Exception,如果任務(wù)沒(méi)有完成,那么get將阻塞并直到任務(wù)完成。如果任務(wù)拋出了一場(chǎng),那么get將該異常封裝為ExecutionException并重新拋出。如果任務(wù)被取消,那么get將拋出CancellationException。如果get拋出了ExecutionException,那么可以通過(guò)getCause來(lái)獲得被封裝的初始異常。
定義了以下方法:
- void shutdown():?jiǎn)?dòng)有序關(guān)機(jī),其中執(zhí)行先前提交的任務(wù),但不接受新任務(wù)。如果調(diào)用已經(jīng)關(guān)閉,則沒(méi)有額外的效果。不會(huì)阻塞等待先前提交的任務(wù)執(zhí)行完成
- List<Runnable> shutdownNow():嘗試停止所有正在執(zhí)行的任務(wù),停止對(duì)等待任務(wù)的處理,并返回等待執(zhí)行的任務(wù)列表。不會(huì)阻塞等待正在執(zhí)行的任務(wù)終止只是盡最大努力停止處理正在執(zhí)行的任務(wù)之外,沒(méi)有任何保證。例如,典型的實(shí)現(xiàn)將通過(guò)Thread.interrupt取消,因此任何未能響應(yīng)中斷的任務(wù)可能永遠(yuǎn)不會(huì)終止
- boolean isShutdown():如果此執(zhí)行器已關(guān)閉(調(diào)用了關(guān)閉方法),則返回true
- boolean isTerminated():如果關(guān)閉后所有任務(wù)都已完成,則返回true。注意,除非先調(diào)用shutdown或shutdownNow,否則isTerminated永遠(yuǎn)不會(huì)為真
- boolean awaitTermination(long timeout, TimeUnit unit):阻塞直到所有任務(wù)在關(guān)機(jī)請(qǐng)求后完成執(zhí)行,或者超時(shí)發(fā)生,或者當(dāng)前線程被中斷,以先發(fā)生的為準(zhǔn)
- <T> Future<T> submit(Callablet task):提交一個(gè)帶返回值的任務(wù)以供執(zhí)行,并返回表示該任務(wù)的掛起結(jié)果的Future。Future的get方法將在成功完成任務(wù)時(shí)返回任務(wù)的結(jié)果
- <T> Future<T> submit(Runnable task, T result):提交可運(yùn)行任務(wù)以供執(zhí)行,并返回表示該任務(wù)的Future。Future的get方法將在成功完成時(shí)返回給定的結(jié)果
- Future<?> submit(Runnable task):提交可運(yùn)行任務(wù)以供執(zhí)行,并返回表示該任務(wù)的Future。Future的get方法將在成功完成時(shí)返回null
- <T> Listfuture<t> invokeAll(Collection? extends Callable<T> tasks):執(zhí)行給定的任務(wù),并在所有任務(wù)完成時(shí)返回保存其狀態(tài)和結(jié)果的future列表。每個(gè)Future對(duì)象的isDone方法都會(huì)返回true。請(qǐng)注意,已完成的任務(wù)可以正常終止,也可以拋出異常終止。如果在執(zhí)行此操作時(shí)修改了給定的集合,則此方法的結(jié)果是未定義的
- <T> T invokeAny(Collection? extends Callable<T> tasks):執(zhí)行給定的任務(wù),如果有成功完成的任務(wù),則返回成功完成的任務(wù)的結(jié)果(即不拋出異常)。在正?;虍惓7祷貢r(shí),未完成的任務(wù)將被取消。如果在執(zhí)行此操作時(shí)修改了給定的集合,則此方法的結(jié)果是未定義的。
三、ThreadPoolExecutor類
ThreadPoolExecutor實(shí)現(xiàn)了ExecutorService(實(shí)際上是繼承了AbstractExecutorService),為了在廣泛的上下文中發(fā)揮作用,該類提供了許多可調(diào)參數(shù)和可擴(kuò)展性掛鉤:
- corePoolSize:指定了線程池中的線程數(shù)量,它的數(shù)量決定了添加的任務(wù)是開(kāi)辟新的線程去執(zhí)行,還是放到workQueue任務(wù)隊(duì)列中去
- maximumPoolSize:指定了線程池中的最大線程數(shù)量,這個(gè)參數(shù)會(huì)根據(jù)你使用的workQueue任務(wù)隊(duì)列的類型,決定線程池會(huì)開(kāi)辟的最大線程數(shù)量
- keepAliveTime:當(dāng)線程池中空閑線程數(shù)量超過(guò)corePoolSize時(shí),多余的線程(救急線程)會(huì)在多長(zhǎng)時(shí)間內(nèi)被銷毀
- unit:keepAliveTime的單位
- workQueue:任務(wù)隊(duì)列,被添加到線程池中,但尚未被執(zhí)行的任務(wù);它一般分為直接提交隊(duì)列、有界任務(wù)隊(duì)列、無(wú)界任務(wù)隊(duì)列、優(yōu)先任務(wù)隊(duì)列幾種
- threadFactory:線程工廠,用于創(chuàng)建線程,一般用默認(rèn)即可
- handler:拒絕策略;當(dāng)任務(wù)太多來(lái)不及處理時(shí),如何拒絕任務(wù)
1、狀態(tài)
- RUNNING(-1<<29):接受新任務(wù)并且處理排隊(duì)任務(wù)
- SHUTDOWN(0<<29):不接受新任務(wù)但是處理排隊(duì)任務(wù)
- STOP(1<<29):不接受新任務(wù)也不處理排隊(duì)任務(wù),同時(shí)中斷處理中的任務(wù)
- TYDING(2<<29):所有任務(wù)被終止,工作線程數(shù)為0之后進(jìn)入該狀態(tài),并且會(huì)執(zhí)行terminated()鉤子函數(shù)
- TERMINATED(3<<29):terminated()鉤子函數(shù)執(zhí)行完畢后
狀態(tài)單調(diào)地隨時(shí)間增加,但不需要達(dá)到每個(gè)狀態(tài)。
- RUNNING->SHUTDOWN:調(diào)用shutdown()
- RUNNING/SHUTDOWN->STOP:調(diào)用shutdownNow()
- STOP->TYDING:當(dāng)隊(duì)列和池都為空時(shí)
- TIDYING -> TERMINATED:terminated()鉤子函數(shù)執(zhí)行完畢后
ThreadPoolExecutor中對(duì)于狀態(tài)的記錄保存在一個(gè)AtomicInteger類型的變量中,其中高三位就是用于記錄線程池的狀態(tài),而低的29位用于記錄線程數(shù)量。
2、Worker
ThreadPoolExecutor中定義了一個(gè)私有靜態(tài)類Worker,其繼承自AbstractQueuedSynchronizer類,并實(shí)現(xiàn)了Runnable接口。其中維護(hù)了線程實(shí)例(Thread)、任務(wù)實(shí)例(Runnable)、線程任務(wù)計(jì)數(shù)器(long)變量。
這個(gè)類適當(dāng)?shù)財(cái)U(kuò)展了AbstractQueuedSynchronizer,以簡(jiǎn)化獲取和釋放圍繞每個(gè)任務(wù)執(zhí)行的鎖。這可以防止中斷,這些中斷旨在喚醒等待任務(wù)的工作線程,而不是中斷正在運(yùn)行的任務(wù)。我們實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的不可重入互斥鎖,而不是使用ReentrantLock,因?yàn)槲覀儾幌Mぷ魅蝿?wù)在調(diào)用setCorePoolSize等池控制方法時(shí)能夠重新獲得鎖。此外,為了在線程實(shí)際開(kāi)始運(yùn)行任務(wù)之前抑制中斷,我們將鎖狀態(tài)初始化為負(fù)值,并在啟動(dòng)時(shí)(在runWorker中)清除它。
3、擴(kuò)展
該類還定義了三個(gè)protected類型的鉤子函數(shù):
- beforeExecute:線程池任務(wù)運(yùn)行前執(zhí)行
- afterExecute:線程池任務(wù)運(yùn)行后執(zhí)行
- terminated:線程池退出后執(zhí)行
這幾個(gè)方法在ThreadPoolExecutor中為空實(shí)現(xiàn)。
四、ForkJoinPool類
Fork/Join框架是Java7提供的一個(gè)用于并行執(zhí)行任務(wù)的框架,是一個(gè)把大任務(wù)分割成若干個(gè)小任務(wù),最終匯總每個(gè)小任務(wù)結(jié)果后得到大任務(wù)結(jié)果的框架。
Fork就是把一個(gè)大任務(wù)切分為若干個(gè)子任務(wù)并行的執(zhí)行,Join就是合并這些子任務(wù)的執(zhí)行結(jié)果,最后得到這個(gè)大任務(wù)的結(jié)果。比如計(jì)算1+2+…+10000,可以分割成10個(gè)子任務(wù),每個(gè)子任務(wù)分別對(duì)1000個(gè)數(shù)進(jìn)行求和,最終匯總這10個(gè)子任務(wù)的結(jié)果。
1、工作竊取算法
ForkJoinPool是運(yùn)行ForkJoinTasks的ExecutorService。**ForkJoinPool與其他類型的ExecutorService的區(qū)別主要在于使用了工作竊取。工作竊取算法是指某個(gè)線程從其他隊(duì)列里竊取任務(wù)來(lái)執(zhí)行。**那么為什么要使用工作竊取算法呢?**假如我們需要做一個(gè)比較大的任務(wù),可以把這個(gè)任務(wù)分割為若干個(gè)互不干擾的子任務(wù),為了減少線程間的競(jìng)爭(zhēng),把這些子任務(wù)分別放到不同的隊(duì)列里,并為每個(gè)隊(duì)列創(chuàng)建一個(gè)單獨(dú)的線程來(lái)執(zhí)行隊(duì)列里的任務(wù),線程和隊(duì)列一一對(duì)應(yīng)。**比如A線程負(fù)責(zé)處理A隊(duì)列里的任務(wù)。但是有的線程會(huì)先把自己隊(duì)列里的任務(wù)干完,而其他線程對(duì)應(yīng)的隊(duì)列里還有任務(wù)等待處理。干完活的線程與其等著,不如去幫其他線程干活,于是它就去其他線程的隊(duì)列里竊取一個(gè)任務(wù)來(lái)執(zhí)行。而這時(shí)它們會(huì)訪問(wèn)同一個(gè)隊(duì)列,所以為了減少竊取任務(wù)線程之間的競(jìng)爭(zhēng),通常會(huì)使用雙端隊(duì)列,被竊取任務(wù)線程永遠(yuǎn)從雙端隊(duì)列的頭部拿任務(wù),而竊取任務(wù)的線程永遠(yuǎn)從雙端隊(duì)列的尾部拿任務(wù)執(zhí)行。
工作竊取算法的優(yōu)點(diǎn):充分利用線程進(jìn)行并行計(jì)算,減少了線程間的競(jìng)爭(zhēng)
工作竊取算法的缺點(diǎn):在某些情況下還是存在競(jìng)爭(zhēng),比如雙端隊(duì)列里只有一個(gè)任務(wù)時(shí)。并且該算法會(huì)消耗了更多的系統(tǒng)資源,比如創(chuàng)建多個(gè)線程和多個(gè)雙端隊(duì)列。
2、Fork/Join的設(shè)計(jì)
想要設(shè)計(jì)一個(gè)Fork/Join框架,需要完成兩個(gè)步驟:
- 分割任務(wù):需要有一個(gè)fork類來(lái)把大任務(wù)分割成子任務(wù),有可能子任務(wù)還是很大,所以還需要不停地分割,直到分割出的子任務(wù)足夠小
- 執(zhí)行任務(wù)并合并結(jié)果:分割的子任務(wù)分別放在雙端隊(duì)列里,然后幾個(gè)啟動(dòng)線程分別從雙端隊(duì)列里獲取任務(wù)執(zhí)行。子任務(wù)執(zhí)行完的結(jié)果都統(tǒng)一放在一個(gè)隊(duì)列里,啟動(dòng)一個(gè)線程從隊(duì)列里拿數(shù)據(jù),然后合并這些數(shù)據(jù)。
Fork/Join使用兩個(gè)類來(lái)完成以上兩件事情:
- ForkJoinTask(抽象類):我們要使用ForkJoin框架,必須首先創(chuàng)建一個(gè)ForkJoin任務(wù)。它提供在任務(wù)中執(zhí)行fork()和join()操作的機(jī)制。通常情況下我們不需要直接繼承ForkJoinTask類,只需要繼承它的子類,F(xiàn)ork/Join框架提供了以下兩個(gè)子類:
- RecursiveAction(抽象類):用于沒(méi)有返回結(jié)果的任務(wù)
- RecursiveTask(抽象類):用于有返回結(jié)果的任務(wù)
- ForkJoinPool:ForkJoinTask需要通過(guò)ForkJoinPool來(lái)執(zhí)行
任務(wù)分割出的子任務(wù)會(huì)添加到當(dāng)前工作線程所維護(hù)的雙端隊(duì)列中,進(jìn)入隊(duì)列的頭部。當(dāng)一個(gè)工作線程的隊(duì)列里暫時(shí)沒(méi)有任務(wù)時(shí),它會(huì)隨機(jī)從其他工作線程的隊(duì)列的尾部獲取一個(gè)線程。
public class CountTask extends RecursiveTask<Integer> { private int start; private int end; public CountTask(int start, int end) { this.start = start; this.end = end; } @Override protected Integer compute() { int sum = 0; // 如果任務(wù)足夠小就計(jì)算任務(wù) boolean canCompute = (end - start) <= THRESHOLD; if (canCompute) { for (int i = start; i <= end; i++) { sum += i; } } else { // 如果任務(wù)大于閾值,就分裂成兩個(gè)子任務(wù)計(jì)算 int middle = (start + end) / 2; CountTask leftTask = new CountTask(start, middle); CountTask rightTask = new CountTask(middle + 1, end); // 執(zhí)行子任務(wù) leftTask.fork(); rightTask.fork(); // 等待子任務(wù)執(zhí)行完,并得到其結(jié)果 int leftResult = leftTask.join(); int rightResult = rightTask.join(); // 合并子任務(wù) sum = leftResult + rightResult; } return sum; } public static void main(String[] args) { ForkJoinPool forkJoinPool = new ForkJoinPool(); CountTask task = new CountTask(1, 4); // 執(zhí)行一個(gè)任務(wù) Future<Integer> result = forkJoinPool.submit(task); try { System.out.println(result.get()); } catch (InterruptedException e) { } catch (ExecutionException e) { } } }
RecursiveTask需要實(shí)現(xiàn)compute方法,在這個(gè)方法里,首先需要判斷任務(wù)是否足夠小,如果足夠小就直接執(zhí)行任務(wù)。如果不足夠小就必須分割成兩個(gè)子任務(wù),每個(gè)子任務(wù)在調(diào)用fork方法時(shí)又會(huì)進(jìn)入compute方法。最后使用join方法等待子任務(wù)執(zhí)行完成并得到其結(jié)果。
ForkJoinTask在執(zhí)行的時(shí)候可能會(huì)拋出異常,但是我們沒(méi)辦法在主線程里直接捕獲異常,所以ForkJoinTask提供了isCompletedAbnormally()方法來(lái)檢查任務(wù)是否已經(jīng)拋出異?;蛞呀?jīng)被取消了,并且可以通過(guò)ForkJoinTask的getException方法來(lái)獲取異常。
getException方法返回Throwable對(duì)象,如果任務(wù)被取消了則返回CancellationException,如果任務(wù)沒(méi)有完成或者沒(méi)有拋出異常則返回null。
3、執(zhí)行原理
ForkJoinPool由ForkJoinTask數(shù)組和ForkJoinWorkerThread數(shù)組組成,F(xiàn)orkJoinTask數(shù)組負(fù)責(zé)存放程序提交給ForkJoinPool的任務(wù),而ForkJoinWorkerThread數(shù)組負(fù)責(zé)執(zhí)行這些任務(wù)。
當(dāng)我們調(diào)用ForkJoinTask的fork方法時(shí),程序會(huì)調(diào)用ForkJoinWorkerThread的pushTask方法異步地執(zhí)行這個(gè)任務(wù),然后立即返回結(jié)果。
pushTask方法把當(dāng)前任務(wù)存放在ForkJoinTask數(shù)組隊(duì)列里,然后再調(diào)用ForkJoinPool的signalWork反復(fù)噶喚醒或創(chuàng)建一個(gè)工作線程來(lái)執(zhí)行任務(wù)。
五、ScheduledThreadPool類
ScheduledThreadPoolExecutor主要用來(lái)在給定的延遲后運(yùn)行任務(wù),或者定期執(zhí)行任務(wù)。ScheduledThreadPoolExecutor使用任務(wù)隊(duì)列DelayQueue封裝了一個(gè)PriorityQueue,PriorityQueue會(huì)對(duì)隊(duì)列中的任務(wù)進(jìn)行排序,執(zhí)行所需時(shí)間短的放在前面先被執(zhí)行(ScheduledFutureTask的time變量小的先執(zhí)行),如果執(zhí)行所需時(shí)間相同則先提交的任務(wù)將被先執(zhí)行(ScheduledFutureTask的squenceNumber變量小的先執(zhí)行)。
1、ScheduledExecutorService
ScheduledThreadPool類繼承自ThreadPoolExecutor,并且實(shí)現(xiàn)了ScheduledExecutorService接口。
ScheduledExecutorService接口定義了幾個(gè)方法:
- ScheduleFuture<?> schedule(Runnable command, long delay, TimeUnit unit):提交在給定延遲后啟用的一次性任務(wù)
- ScheduleFuture schedule(Callable command, long delay, TimeUnit unit):提交在給定延遲之后啟用的帶返回值的一次性任務(wù)
- ScheduleFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit):提交一個(gè)周期性任務(wù),任務(wù)開(kāi)始時(shí)進(jìn)行計(jì)時(shí)(如果任務(wù)執(zhí)行時(shí)間過(guò)長(zhǎng)甚至超過(guò)period時(shí)間,會(huì)導(dǎo)致任務(wù)連續(xù)執(zhí)行)
- ScheduleFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit):提交一個(gè)周期性任務(wù),任務(wù)執(zhí)行完成之后才進(jìn)行計(jì)時(shí)
ScheduledFuture繼承自Delayed接口和Future接口,自己本身沒(méi)有定義新的方法。
Delayed接口是一個(gè)混合風(fēng)格的接口,用于標(biāo)記應(yīng)該在給定延遲后執(zhí)行的對(duì)象。這個(gè)接口定義了一個(gè)getDelay方法,用于返回剩余的延遲時(shí)間。此外這個(gè)接口繼承了Comparable接口,意味著此接口的實(shí)現(xiàn)必須定義一個(gè)compareTo方法,該方法提供與其getDelay方法一致的排序
2、比較Timer
Timer對(duì)系統(tǒng)時(shí)鐘的變化敏感,ScheduledThreadPoolExecutor不是
Timer只有一個(gè)執(zhí)行線程,因此長(zhǎng)時(shí)間運(yùn)行的任務(wù)可以延遲其他任務(wù)。 ScheduledThreadPoolExecutor可以配置任意數(shù)量的線程。 此外,如果你想(通過(guò)提供 ThreadFactory),你可以完全控制創(chuàng)建的線程
在TimerTask中拋出的運(yùn)行時(shí)異常會(huì)殺死一個(gè)線程,從而導(dǎo)致 Timer 死機(jī),即計(jì)劃任務(wù)將不再運(yùn)行。ScheduledThreadExecutor不僅捕獲運(yùn)行時(shí)異常,還允許您在需要時(shí)處理它們(通過(guò)重寫(xiě)afterExecute方法ThreadPoolExecutor)。拋出異常的任務(wù)將被取消,但其他任務(wù)將繼續(xù)運(yùn)行
六、Executors類
Executors是Java中用于創(chuàng)建線程池的工廠類,它提供了一系列的靜態(tài)工廠方法,用于創(chuàng)建不同類型的線程池。這些工廠方法隱藏了線程池的復(fù)雜性,使得線程池的創(chuàng)建變得非常簡(jiǎn)單。Executors工廠類提供的線程池有以下幾種類型:
- newCachedThreadPool():CachedThreadPool的corePoolSize 被設(shè)置為0,maximumPoolSize被設(shè)置為Integer.MAX.VALUE,即它是無(wú)界的,這也就意味著如果主線程提交任務(wù)的速度高于maximumPool中線程處理任務(wù)的速度時(shí),CachedThreadPool會(huì)不斷創(chuàng)建新的線程。極端情況下,這樣會(huì)導(dǎo)致耗盡 cpu和內(nèi)存資源
- newFixedThreadPool(int nThreads):創(chuàng)建一個(gè)固定大小的線程池,其中包含指定數(shù)量的線程。線程數(shù)量是固定的,不會(huì)自動(dòng)擴(kuò)展,即沒(méi)有救急線程
- newSingleThreadExecutor():創(chuàng)建一個(gè)單線程的線程池。這個(gè)線程池中只包含一個(gè)線程,用于串行執(zhí)行任務(wù)。適用于需要按順序執(zhí)行任務(wù)的場(chǎng)景
- newScheduledThreadPool(int corePoolSize):創(chuàng)建一個(gè)固定大小的線程池,用于定時(shí)執(zhí)行任務(wù)。線程數(shù)量固定,不會(huì)自動(dòng)擴(kuò)展。適用于定時(shí)執(zhí)行任務(wù)的場(chǎng)景
- newSingleThreadScheduledExecutor():創(chuàng)建一個(gè)單線程的定時(shí)執(zhí)行線程池。只包含一個(gè)線程,用于串行定時(shí)執(zhí)行任務(wù)
- newWorkStealingPool(int parallelism):該線程池維護(hù)足夠的線程以支持給定的并行級(jí)別,并且可以使用多個(gè)隊(duì)列來(lái)減少爭(zhēng)用。并行性級(jí)別對(duì)應(yīng)于積極參與或可用參與任務(wù)處理的最大線程數(shù)。實(shí)際的線程數(shù)可以動(dòng)態(tài)地增加和減少。工作竊取池不能保證所提交任務(wù)的執(zhí)行順序
除此之外還提供了創(chuàng)建ThreadFactory實(shí)例,將Runnable實(shí)例轉(zhuǎn)換為Callable實(shí)例等方法。
到此這篇關(guān)于Java多線程中的Executor框架解析的文章就介紹到這了,更多相關(guān)Java的Executor框架內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
spring?NamedContextFactory在Fegin配置及使用詳解
在我們?nèi)粘m?xiàng)目中,使用FeignClient實(shí)現(xiàn)各系統(tǒng)接口調(diào)用變得更加簡(jiǎn)單,?在各個(gè)系統(tǒng)集成過(guò)程中,難免會(huì)遇到某些系統(tǒng)的Client需要特殊的配置、返回讀取等需求。Feign使用NamedContextFactory來(lái)為每個(gè)Client模塊構(gòu)造單獨(dú)的上下文(ApplicationContext)2023-11-11springboot如何根據(jù)不同的日志級(jí)別顯示不同的顏色
這篇文章主要介紹了springboot如何根據(jù)不同的日志級(jí)別顯示不同的顏色問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08Mybatis-plus使用注解 @TableField(exist = false)
這篇文章主要介紹了Mybatis-plus使用注解 @TableField(exist = false),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03mybatis關(guān)聯(lián)關(guān)系映射的實(shí)現(xiàn)
MyBatis的關(guān)聯(lián)關(guān)系映射在復(fù)雜數(shù)據(jù)模型中至關(guān)重要,使開(kāi)發(fā)人員能夠以最靈活的方式滿足不同項(xiàng)目的需求,本文就來(lái)介紹一下mybatis關(guān)聯(lián)關(guān)系映射的實(shí)現(xiàn),感興趣的可以了解一下2023-09-09Java實(shí)現(xiàn)解析并生成xml原理實(shí)例詳解
這篇文章主要介紹了Java實(shí)現(xiàn)解析并生成xml原理實(shí)例詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06Java中WeakHashMap和HashMap的區(qū)別詳解
這篇文章主要介紹了Java中WeakHashMap和HashMap的區(qū)別詳解,WeakHashMap和HashMap一樣,WeakHashMap也是一個(gè)散列表,它存儲(chǔ)的內(nèi)容也是鍵值對(duì)(key-value)映射,而且鍵和值都可以為null,需要的朋友可以參考下2023-09-09java selenium 常見(jiàn)web UI 元素操作及API使用
本文主要介紹java selenium 常見(jiàn)web UI 元素操作,這里幫大家整理了相關(guān)資料并附示例代碼,有需要的小伙伴可以參考下2016-08-08使用Swagger2實(shí)現(xiàn)自動(dòng)生成RESTful?API文檔
在開(kāi)發(fā)?RESTful?API?的過(guò)程中,文檔是非常重要的一部分,可以幫助開(kāi)發(fā)者了解?API?的功能和使用方法,本文將使用Swagger2?實(shí)現(xiàn)自動(dòng)生成?RESTful?API?文檔,需要的可以參考一下2023-06-06淺談Java中的重載,重寫(xiě),多態(tài),靜態(tài)綁定、動(dòng)態(tài)綁定
這篇文章主要介紹了淺談Java中的重載,重寫(xiě),多態(tài),靜態(tài)綁定、動(dòng)態(tài)綁定,具有一定借鑒價(jià)值2018-01-01