一文弄懂Java中ThreadPoolExecutor
一、ThreadPoolExecutor類講解
1、線程池狀態(tài):
五種狀態(tài):
線程池 的狀態(tài) | 說明 |
RUNNING | 允許提交并處理任務(wù) |
SHUTDOWN | 不允許提交新的任務(wù),但是會處理完已提交的任務(wù) |
STOP | 不允許提交新的任務(wù),也不會處理阻塞隊列中未執(zhí)行的任務(wù), 并設(shè)置正在執(zhí)行的線程的中斷標(biāo)志位 |
TIDYING | 所有任務(wù)執(zhí)行完畢,池中工作的線程數(shù)為0,等待執(zhí)行terminated()勾子方法 |
TERMINATED | terminated()勾子方法執(zhí)行完畢 |
- 線程池的shutdown() 方法,將線程池由 RUNNING(運(yùn)行狀態(tài))轉(zhuǎn)換為 SHUTDOWN狀態(tài)
- 線程池的shutdownNow()方法,將線程池由RUNNING 或 SHUTDOWN 狀態(tài)轉(zhuǎn)換為 STOP 狀態(tài)。
注:SHUTDOWN 狀態(tài) 和 STOP 狀態(tài) 先會轉(zhuǎn)變?yōu)?TIDYING 狀態(tài),最終都會變?yōu)?TERMINATED
2、ThreadPoolExecutor構(gòu)造函數(shù):
ThreadPoolExecutor繼承自AbstractExecutorService,而AbstractExecutorService實現(xiàn)了ExecutorService接口。
接下來我們分別講解這些參數(shù)的含義。
2.1)線程池工作原理:
- corePoolSize :線程池中核心線程數(shù)的最大值
- maximumPoolSize :線程池中能擁有最多線程數(shù)
- workQueue:用于緩存任務(wù)的阻塞隊列
當(dāng)調(diào)用線程池execute() 方法添加一個任務(wù)時,線程池會做如下判斷:
- 如果有空閑線程,則直接執(zhí)行該任務(wù);
- 如果沒有空閑線程,且當(dāng)前運(yùn)行的線程數(shù)少于corePoolSize,則創(chuàng)建新的線程執(zhí)行該任務(wù);
- 如果沒有空閑線程,且當(dāng)前的線程數(shù)等于corePoolSize,同時阻塞隊列未滿,則將任務(wù)入隊列,而不添加新的線程;
- 如果沒有空閑線程,且阻塞隊列已滿,同時池中的線程數(shù)小于maximumPoolSize ,則創(chuàng)建新的線程執(zhí)行任務(wù);
- 如果沒有空閑線程,且阻塞隊列已滿,同時池中的線程數(shù)等于maximumPoolSize ,則根據(jù)構(gòu)造函數(shù)中的 handler 指定的策略來拒絕新的任務(wù)。
2.2)KeepAliveTime:
- keepAliveTime :表示空閑線程的存活時間
- TimeUnit unit :表示keepAliveTime的單位
當(dāng)一個線程無事可做,超過一定的時間(keepAliveTime)時,線程池會判斷,如果當(dāng)前運(yùn)行的線程數(shù)大于 corePoolSize,那么這個線程就被停掉。所以線程池的所有任務(wù)完成后,它最終會收縮到 corePoolSize 的大小。
注:如果線程池設(shè)置了allowCoreThreadTimeout參數(shù)為true(默認(rèn)false),那么當(dāng)空閑線程超過keepaliveTime后直接停掉。(不會判斷線程數(shù)是否大于corePoolSize)即:最終線程數(shù)會變?yōu)?。
2.3)workQueue 任務(wù)隊列:
- workQueue :它決定了緩存任務(wù)的排隊策略
ThreadPoolExecutor線程池推薦了三種等待隊列,它們是:SynchronousQueue 、LinkedBlockingQueue 和 ArrayBlockingQueue。
1)有界隊列:
- SynchronousQueue :一個不存儲元素的阻塞隊列,每個插入操作必須等到另一個線程調(diào)用移除操作,否則插入操作一直處于 阻塞狀態(tài),吞吐量通常要高于LinkedBlockingQueue,靜態(tài)工廠方法 Executors.newCachedThreadPool 使用了這個隊列。
- ArrayBlockingQueue:一個由數(shù)組支持的有界阻塞隊列。此隊列按 FIFO(先進(jìn)先出)原則對元素進(jìn)行排序。一旦創(chuàng)建了這樣的緩存區(qū),就不能再增加其容量。試圖向已滿隊列中放入元素會導(dǎo)致操作受阻塞;試圖從空隊列中提取元素將導(dǎo)致類似阻塞。
2)無界隊列:
- LinkedBlockingQueue:基于鏈表結(jié)構(gòu)的無界阻塞隊列,它可以指定容量也可以不指定容量(實際上任何無限容量的隊列/棧都是有容量的,這個容量就是Integer.MAX_VALUE)
- PriorityBlockingQueue:是一個按照優(yōu)先級進(jìn)行內(nèi)部元素排序的無界阻塞隊列。隊列中的元素必須實現(xiàn) Comparable 接口,這樣才能通過實現(xiàn)compareTo()方法進(jìn)行排序。優(yōu)先級最高的元素將始終排在隊列的頭部;PriorityBlockingQueue 不會保證優(yōu)先級一樣的元素的排序。
注意:keepAliveTime和maximumPoolSize及BlockingQueue的類型均有關(guān)系。如果BlockingQueue是無界的,那么永遠(yuǎn)不會觸發(fā)maximumPoolSize,自然keepAliveTime也就沒有了意義。
2.4)threadFactory:
- threadFactory :指定創(chuàng)建線程的工廠。(可以不指定)
如果不指定線程工廠時,ThreadPoolExecutor 會使用ThreadPoolExecutor.defaultThreadFactory 創(chuàng)建線程。默認(rèn)工廠創(chuàng)建的線程:同屬于相同的線程組,具有同為 Thread.NORM_PRIORITY 的優(yōu)先級,以及名為 “pool-XXX-thread-” 的線程名(XXX為創(chuàng)建線程時順序序號),且創(chuàng)建的線程都是非守護(hù)進(jìn)程。
2.5)handler 拒絕策略:
- handler :表示當(dāng) workQueue 已滿,且池中的線程數(shù)達(dá)到 maximumPoolSize 時,線程池拒絕添加新任務(wù)時采取的策略。(可以不指定)
策略 | BB |
ThreadPoolExecutor.AbortPolicy() | 拋出RejectedExecutionException異常。默認(rèn)策略 |
ThreadPoolExecutor.CallerRunsPolicy() | 由向線程池提交任務(wù)的線程來執(zhí)行該任務(wù) |
ThreadPoolExecutor.DiscardPolicy() | 拋棄當(dāng)前的任務(wù) |
ThreadPoolExecutor.DiscardOldestPolicy() | 拋棄最舊的任務(wù)(最先提交而沒有得到執(zhí)行的任務(wù)) |
最科學(xué)的的還是 AbortPolicy 提供的處理方式:拋出異常,由開發(fā)人員進(jìn)行處理。
3、常用方法:
除了在創(chuàng)建線程池時指定上述參數(shù)的值外,還可在線程池創(chuàng)建以后通過如下方法進(jìn)行設(shè)置。
此外,還有一些方法:
- getCorePoolSize():返回線程池的核心線程數(shù),這個值是一直不變的,返回在構(gòu)造函數(shù)中設(shè)置的coreSize大小;
- getMaximumPoolSize():返回線程池的最大線程數(shù),這個值是一直不變的,返回在構(gòu)造函數(shù)中設(shè)置的coreSize大?。?/li>
- getLargestPoolSize():記錄了曾經(jīng)出現(xiàn)的最大線程個數(shù)(水位線);
- getPoolSize():線程池中當(dāng)前線程的數(shù)量;
- getActiveCount():Returns the approximate(近似) number of threads that are actively executing tasks;
- prestartAllCoreThreads():會啟動所有核心線程,無論是否有待執(zhí)行的任務(wù),線程池都會創(chuàng)建新的線程,直到池中線程數(shù)量達(dá)到 corePoolSize;
- prestartCoreThread():會啟動一個核心線程(同上);
- allowCoreThreadTimeOut(true):允許核心線程在KeepAliveTime時間后,退出;
4、Executors類:
Executors類的底層實現(xiàn)便是ThreadPoolExecutor! Executors 工廠方法有:
- Executors.newCachedThreadPool():無界線程池,可以進(jìn)行自動線程回收
- Executors.newFixedThreadPool(int):固定大小線程池
- Executors.newSingleThreadExecutor():單個后臺線程
它們均為大多數(shù)使用場景預(yù)定義了設(shè)置。不過在阿里java文檔中說明,盡量不要用該類創(chuàng)建線程池。
二、線程池相關(guān)接口介紹:
1、ExecutorService接口:
該接口是真正的線程池接口。上面的ThreadPoolExecutor以及下面的ScheduledThreadPoolExecutor都是該接口的實現(xiàn)類。改接口常用方法:
- Future<?> submit(Runnable task):提交Runnable任務(wù)到線程池,返回Future對象,由于Runnable沒有返回值,也就是說調(diào)用Future對象get()方法返回null;
- <T> Future<T> submit(Callable<T> task):提交Callable任務(wù)到線程池,返回Future對象,調(diào)用Future對象get()方法可以獲取Callable的返回值;
- <T> Future<T> submit(Runnable task,T result):提交Runnable任務(wù)到線程池,返回Future對象,調(diào)用Future對象get()方法可以獲取Runnable的參數(shù)值;
- invokeAll(collection of tasks)/invokeAll(collection of tasks, long timeout, TimeUnit unit):invokeAll會按照任務(wù)集合中的順序?qū)⑺械腇uture添加到返回的集合中,該方法是一個阻塞的方法。只有當(dāng)所有的任務(wù)都執(zhí)行完畢時,或者調(diào)用線程被中斷,又或者超出指定時限時,invokeAll方法才會返回。當(dāng)invokeAll返回之后每個任務(wù)要么返回,要么取消,此時客戶端可以調(diào)用get/isCancelled來判斷具體是什么情況。
- invokeAny(collection of tasks)/invokeAny(collection of tasks, long timeout, TimeUnit unit):阻塞的方法,不會返回 Future 對象,而是返回集合中某一個Callable 對象的結(jié)果,而且無法保證調(diào)用之后返回的結(jié)果是哪一個 Callable,如果一個任務(wù)運(yùn)行完畢或者拋出異常,方法會取消其它的 Callable 的執(zhí)行。和invokeAll區(qū)別是只要有一個任務(wù)執(zhí)行完了,就把結(jié)果返回,并取消其他未執(zhí)行完的任務(wù);同樣,也帶有超時功能;
- shutdown():在完成已提交的任務(wù)后關(guān)閉服務(wù),不再接受新任;
- shutdownNow():停止所有正在執(zhí)行的任務(wù)并關(guān)閉服務(wù);
- isTerminated():測試是否所有任務(wù)都執(zhí)行完畢了;
- isShutdown():測試是否該ExecutorService已被關(guān)閉。
1.1)submit方法示例:
我們知道,線程池接口中有以下三個主要方法,接下來我們看一下具體示例:
1)Callable:
public static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 50, 300, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(50), new ThreadFactory(){ public Thread newThread(Runnable r) { return new Thread(r, "schema_task_pool_" + r.hashCode()); }}, new ThreadPoolExecutor.DiscardOldestPolicy()); public static void callableTest() { int a = 1; //callable Future<Boolean> future = threadPool.submit(new Callable<Boolean>(){ @Override public Boolean call() throws Exception { int b = a + 100; System.out.println(b); return true; } }); try { System.out.println("feature.get"); Boolean boolean1 = future.get(); System.out.println(boolean1); } catch (InterruptedException e) { System.out.println("InterruptedException..."); e.printStackTrace(); } catch (ExecutionException e) { System.out.println("execute exception..."); e.printStackTrace(); } }
2)Runnable:
public static void runnableTest() { int a = 1; //runnable Future<?> future1 = threadPool.submit(new Runnable(){ @Override public void run() { int b = a + 100; System.out.println(b); } }); try { System.out.println("feature.get"); Object x = future1.get(900,TimeUnit.MILLISECONDS); System.out.println(x);//null } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { System.out.println("execute exception..."); e.printStackTrace(); } catch (TimeoutException e) { e.printStackTrace(); } }
3)Runnable+result:
class RunnableTask implements Runnable { Person p; RunnableTask(Person p) { this.p = p; } @Override public void run() { p.setId(1); p.setName("Runnable Task..."); } } class Person { private Integer id; private String name; public Person(Integer id, String name) { super(); this.id = id; this.name = name; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Person [id=" + id + ", name=" + name + "]"; } } public static void runnableTest2() { //runnable + result Person p = new Person(0,"person"); Future<Person> future2 = threadPool.submit(new RunnableTask(p),p); try { System.out.println("feature.get"); Person person = future2.get(); System.out.println(person); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } }
1.2)線程池執(zhí)行時,Callable的call方法(Runnable的run方法)拋出異常后,會出現(xiàn)什么?
在上面的例子中我們可以看到,線程池?zé)o論是執(zhí)行Callable還是Runnable,調(diào)用返回的Future對象get()方法時需要處理兩種異常(如果是調(diào)用get(timeout)方法,需要處理三種異常),如下:
//在線程池上運(yùn)行 Future<Object> future = threadPool.submit(callable); try { System.out.println("feature.get"); Object x = future.get(900,TimeUnit.MILLISECONDS); System.out.println(x); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { System.out.println("execute exception..."); e.printStackTrace(); } catch (TimeoutException e) { e.printStackTrace(); }
- 如果get方法被打斷,進(jìn)入InterruptedException異常;
- 如果線程執(zhí)行過程(call、run方法)中拋出異常,進(jìn)入ExecutionException異常;
- 如果get方法超時,進(jìn)入TimeoutException異常;
1.3)submit()和execute()方法區(qū)別:
ExecutorService、ScheduledExecutorService接口的submit()和execute()方法都是把任務(wù)提交到線程池中,但二者的區(qū)別是
- 接收的參數(shù)不一樣,execute只能接收Runnable類型、submit可以接收Runnable和Callable兩種類型;
- submit有返回值,而execute沒有返回值;submit方便Exception處理;
1)submit方法內(nèi)部實現(xiàn):
其實submit方法也沒有什么神秘的,就是將我們的任務(wù)封裝成了RunnableFuture接口(繼承了Runnable、Future接口),再調(diào)用execute方法,我們看源碼:
public Future<?> submit(Runnable task) { if (task == null) throw new NullPointerException(); RunnableFuture<Void> ftask = newTaskFor(task, null); //轉(zhuǎn)成 RunnableFuture,傳的result是null execute(ftask); return ftask; } public <T> Future<T> submit(Runnable task, T result) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task, result); execute(ftask); return ftask; } public <T> Future<T> submit(Callable<T> task) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task); execute(ftask); return ftask; }
2)newTaskFor方法內(nèi)部實現(xiàn):
newTaskFor方法是new了一個FutureTask返回,所以三個方法其實都是把task轉(zhuǎn)成FutureTask,如果task是Callable,就直接賦值,如果是Runnable 就轉(zhuǎn)為Callable再賦值。
當(dāng)submit參數(shù)是Callable 時:
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { return new FutureTask<T>(callable); } public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; }
當(dāng)submit參數(shù)是Runnable時:
// 按順序看,層層調(diào)用 protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) { return new FutureTask<T>(runnable, value); } public FutureTask(Runnable runnable, V result) { this.callable = Executors.callable(runnable, result); //轉(zhuǎn) runnable 為 callable this.state = NEW; } // 以下為Executors中的方法 public static <T> Callable<T> callable(Runnable task, T result) { if (task == null) throw new NullPointerException(); return new RunnableAdapter<T>(task, result); } static final class RunnableAdapter<T> implements Callable<T> { //適配器 final Runnable task; final T result; RunnableAdapter(Runnable task, T result) { this.task = task; this.result = result; } public T call() { task.run(); return result; } }
看了源碼就揭開了神秘面紗了,就是因為Future需要返回結(jié)果,所以內(nèi)部task必須是Callable,如果task是Runnable 就偷天換日,在Runnable 外面包個Callable馬甲,返回的結(jié)果在構(gòu)造時就寫好。
參考:搞懂Runnable、Callable、Future、FutureTask 及應(yīng)用_趕路人兒的博客-CSDN博客
1.4)ScheduledExecutorService接口:
繼承ExecutorService,并且提供了按時間安排執(zhí)行任務(wù)的功能,它提供的方法主要有:
- schedule(task, initDelay): 安排所提交的Callable或Runnable任務(wù)在initDelay指定的時間后執(zhí)行;
- scheduleAtFixedRate():安排所提交的Runnable任務(wù)按指定的間隔重復(fù)執(zhí)行;
- scheduleWithFixedDelay():安排所提交的Runnable任務(wù)在每次執(zhí)行完后,等待delay所指定的時間后重復(fù)執(zhí)行;
注:該接口的實現(xiàn)類是ScheduledThreadPoolExecutor。
2、Callable接口:
jdk1.5以后創(chuàng)建線程可以通過一下方式:
- 繼承Thread類,實現(xiàn)void run()方法;
- 實現(xiàn)Runnable接口,實現(xiàn)void run()方法;
- 實現(xiàn)Callable接口,實現(xiàn)V call() Throws Exception方法
1)Callable和Runnale接口區(qū)別:
- Callable可以拋出異常,和Future、FutureTask配合可以用來獲取異步執(zhí)行的結(jié)果;
- Runnable沒有返回結(jié)果,異常只能內(nèi)部消化;
2)執(zhí)行Callable的線程的方法可以通過以下兩種方式:
- 借助FutureTask,使用Thread的start方法來執(zhí)行;
- 加入到線程池中,使用線程池的execute或submit執(zhí)行;
注:Callable無法直接使用Thread來執(zhí)行;
我們都知道,Callable帶有返回值的,如果我們不需要返回值,卻又想用Callable該如何做?
jdk中有個Void類型(大寫V),但必須也要return null。
threadpool.submit(new Callable<Void>() { @Override public Void call() { //... return null; } });
3)通過Executors工具類可以把Runnable接口轉(zhuǎn)換成Callable接口:
Executors中的callable方法可以將Runnable轉(zhuǎn)成Callable,如下:
public static <T> Callable<T> callable(Runnable task, T result) { if (task == null) throw new NullPointerException(); return new RunnableAdapter<T>(task, result); }
RunnableAdapter類在上面已經(jīng)看過源碼,原理就是將返回值result作為成員變量,通過參數(shù)傳遞進(jìn)去,進(jìn)而實現(xiàn)了Runnable可以返回值。
示例:
public static void test5() { Person p = new Person(0,"person"); RunnableTask runnableTask = new RunnableTask(p);//創(chuàng)建runnable Callable<Person> callable = Executors.callable(runnableTask,p);//轉(zhuǎn)換 Future<Person> future1 = threadPool.submit(callable);//在線程池上執(zhí)行Callable try { Person person = future1.get(); System.out.println(person); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } Runnable runnable = new Runnable() {//創(chuàng)建Runnable @Override public void run() { } }; Callable<Object> callable2 = Executors.callable(runnable);//轉(zhuǎn)換 Future<Object> future2 = threadPool.submit(callable2);//在線程池上執(zhí)行Callable try { Object o = future2.get(); System.out.println(o); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } }
3、Future接口:
3.1)Future是用來獲取異步計算結(jié)果的接口,常用方法:
- boolean cancel(boolean mayInterruptIfRunning):試圖取消對此任務(wù)的執(zhí)行。如果任務(wù)已完成、或已取消,或者由于某些其他原因而無法取消,則此嘗試將失敗。當(dāng)調(diào)用 cancel 時,如果調(diào)用成功,而此任務(wù)尚未啟動,則此任務(wù)將永不運(yùn)行。如果任務(wù)已經(jīng)啟動,則 mayInterruptIfRunning 參數(shù)確定是否應(yīng)該以試圖停止任務(wù)的方式來中斷執(zhí)行此任務(wù)的線程。此方法返回后,對 isDone() 的后續(xù)調(diào)用將始終返回 true。如果此方法返回 true,則對 isCancelled() 的后續(xù)調(diào)用將始終返回 true。
- boolean isCancelled():如果在任務(wù)正常完成前將其取消,則返回 true。
- boolean isDone():如果任務(wù)已完成,則返回 true,可能由于正常終止、異?;蛉∠瓿?,在所有這些情況中,此方法都將返回 true。
- V get()throws InterruptedException,ExecutionException:獲取異步結(jié)果,此方法會一直阻塞等到計算完成;
- V get(long timeout,TimeUnit unit) throws InterruptedException,ExecutionException,TimeoutException:獲取異步結(jié)果,此方法會在指定時間內(nèi)一直阻塞等到計算完成,超時后會拋出超時異常。
通過方法分析我們也知道實際上Future提供了3種功能:
- 能夠中斷執(zhí)行中的任務(wù);
- 判斷任務(wù)是否執(zhí)行完成;
- 獲取任務(wù)執(zhí)行完成后額結(jié)果。
但是Future只是一個接口,我們無法直接創(chuàng)建對象,因此就需要其實現(xiàn)類FutureTask登場啦。
3.2)FutureTask類:
1)FutureTask類的實現(xiàn):
public class FutureTask<V> implements RunnableFuture<V> { //... } public interface RunnableFuture<V> extends Runnable, Future<V> { /** * Sets this Future to the result of its computation * unless it has been cancelled. */ void run(); }
FutureTask實現(xiàn)了Runnable、Future兩個接口。由于FutureTask實現(xiàn)了Runnable,因此它既可以通過Thread包裝來直接執(zhí)行,也可以提交給ExecuteService來執(zhí)行。并且還可以直接通過get()函數(shù)獲取執(zhí)行結(jié)果,該函數(shù)會阻塞,直到結(jié)果返回。因此FutureTask既是Future、Runnable,又是包裝了Callable( 如果是Runnable最終也會被轉(zhuǎn)換為Callable ), 它是這兩者的合體。
2)FutureTask的構(gòu)造函數(shù):
public FutureTask(Callable<V> callable) { } public FutureTask(Runnable runnable, V result) { }
3.3)示例:(FutureTask兩種構(gòu)造函數(shù)、以及在Thread和線程池上運(yùn)行)
1)FutureTask包裝過的Callable在Thread、線程池上執(zhí)行:
public static void test3() { int a = 1,b = 2; Callable<Integer> callable = new Callable<Integer>() { @Override public Integer call() throws Exception { return a + b; } }; //通過futureTask來執(zhí)行Callable FutureTask<Integer> futureTask = new FutureTask<>(callable); //1.使用Thread執(zhí)行線程 new Thread(futureTask).start(); try { Integer integer = futureTask.get(); System.out.println(integer); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } //2.使用線程池執(zhí)行線程 Executors.newFixedThreadPool(1).submit(futureTask); threadPool.shutdown(); try { Integer integer = futureTask.get(); System.out.println(integer); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } }
2)FutureTask包裝過的Runnable在Thread、線程池上執(zhí)行:
public static void test4() { Person p = new Person(0,"person"); RunnableTask runnableTask = new RunnableTask(p); //創(chuàng)建futureTask來執(zhí)行Runnable FutureTask<Person> futureTask = new FutureTask<>(runnableTask,p); //1.使用Thread執(zhí)行線程 new Thread(futureTask).start(); try { Person x = futureTask.get(); System.out.println(x); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } //2.使用線程池執(zhí)行線程 threadPool.submit(futureTask); threadPool.shutdown(); try { Person y = futureTask.get(); System.out.println(y); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } }
Person、RunnableTask類同上面的示例中。
到此這篇關(guān)于一文弄懂Java中ThreadPoolExecutor的文章就介紹到這了,更多相關(guān)Java ThreadPoolExecut內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java線程池?ThreadPoolExecutor?詳解
- Java多線程ThreadPoolExecutor詳解
- Java線程池ThreadPoolExecutor源碼深入分析
- java高并發(fā)ThreadPoolExecutor類解析線程池執(zhí)行流程
- java高并發(fā)ScheduledThreadPoolExecutor與Timer區(qū)別
- 徹底搞懂java并發(fā)ThreadPoolExecutor使用
- Java多線程編程基石ThreadPoolExecutor示例詳解
- 源碼分析Java中ThreadPoolExecutor的底層原理
- 一文搞懂Java的ThreadPoolExecutor原理
相關(guān)文章
Spring整合TimerTask實現(xiàn)定時任務(wù)調(diào)度
這篇文章主要介紹了Spring整合TimerTask實現(xiàn)定時任務(wù)調(diào)度的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-12-12SpringBoot整合WebSocket實現(xiàn)后端向前端發(fā)送消息的實例代碼
WebSocket使得客戶端和服務(wù)器之間的數(shù)據(jù)交換變得更加簡單,允許服務(wù)端主動向客戶端推送數(shù)據(jù),下面這篇文章主要給大家介紹了關(guān)于SpringBoot整合WebSocket實現(xiàn)后端向前端發(fā)送消息的相關(guān)資料,需要的朋友可以參考下2023-03-03Spring Boot實現(xiàn)圖片上傳/加水印一把梭操作實例代碼
這篇文章主要給大家介紹了關(guān)于Spring Boot實現(xiàn)圖片上傳/加水印一把梭操作的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-11-11Spring定時任務(wù)中@PostConstruct被多次執(zhí)行異常的分析與解決
這篇文章主要給大家介紹了關(guān)于Spring定時任務(wù)中@PostConstruct被多次執(zhí)行異常的分析與解決方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-10-10