摘要:一個(gè)線程池包含很多準(zhǔn)備運(yùn)行的空閑線程,每當(dāng)執(zhí)行完畢后,線程不會死亡而是回到線程池準(zhǔn)備為下一個(gè)請求提供服務(wù)。另一個(gè)使用線程池的理由是減少并發(fā)線程數(shù)。創(chuàng)建大量線程會大大降低性能甚至拖垮虛擬機(jī)。
【Future的概念
interface Future
public interface Future{ //取消計(jì)算,如果尚未開始,直接取消不再運(yùn)算,如果正在進(jìn)行:mayInterruptIfRunning等于true就會被中斷。注意一點(diǎn):只要調(diào)用了cancle(),無論任務(wù)是否能夠執(zhí)行,再調(diào)用get()都會出現(xiàn)cancledException,這是由于Future.state的狀態(tài)被置為CANCELLED = 4,所致的; boolean cancel(boolean mayInterruptIfRunning) //判斷計(jì)算是否取消。 boolean isCancelled(); //判斷計(jì)算是否完成,如果計(jì)算完成返回true,否則返回false boolean isDone(); //獲得異步計(jì)算的結(jié)果,如果在調(diào)用get()的時(shí)候結(jié)果還沒有計(jì)算出來,調(diào)用線程將被阻塞。 V get() throws InterruptedException, ExecutionException; //獲得異步計(jì)算的結(jié)果,如果在調(diào)用get()的時(shí)候結(jié)果還沒有計(jì)算出來,調(diào)用線程將被阻塞。如果調(diào)用超時(shí)將會拋出TimeoutException。 V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
補(bǔ)充:
- FutureTask:包裝器,可以將Callable轉(zhuǎn)換成為Future與Runnable,它同時(shí)實(shí)現(xiàn)了二者的接口。 - Callable:可以為異步方法返回計(jì)算結(jié)果。【下面我們實(shí)現(xiàn)一種基于Future的場景
public class Exercise { private SimpleApplicationService simpleApplicationService = new SimpleApplicationService(); @Test public void futureTest() throws InterruptedException, ExecutionException { long st = System.currentTimeMillis(); //這里本是一callable接口實(shí)現(xiàn),我用lambda方便一點(diǎn) FutureTask result = new FutureTask【反思一些問題:(() -> simpleApplicationService.query()); //記住我們的任務(wù)都與需要開啟一個(gè)新的線程 Thread t = new Thread(result); t.start(); long end = System.currentTimeMillis(); System.out.println("耗時(shí):" + (end - st) + " 結(jié)果:" + result.get()); } } public class SimpleApplicationService { public String query() throws InterruptedException { System.out.println("開始查詢"); Thread.sleep(2000); return "query result"; } }
每個(gè)異步方法都需要新開啟一個(gè)線程這樣很消耗資源。
每次都要new一個(gè)thread挺麻煩的。
【解決方案:使用線程池構(gòu)建一個(gè)新的線程是有代價(jià)的,因?yàn)樵O(shè)計(jì)到與操作系統(tǒng)的交互。如果程序中創(chuàng)建了大量的并且生命周期很短的線程,我們應(yīng)該使用線程池。
一個(gè)線程池包含很多準(zhǔn)備運(yùn)行的空閑線程,每當(dāng)run()執(zhí)行完畢后,線程不會死亡而是回到線程池準(zhǔn)備為下一個(gè)請求提供服務(wù)。
另一個(gè)使用線程池的理由是減少并發(fā)線程數(shù)。創(chuàng)建大量線程會大大降低性能甚至拖垮虛擬機(jī)。
【Executor介紹Executors類有許多靜態(tài)方法可創(chuàng)建線程池。例如:
Executors.newCachedThreadPool():對于一個(gè)任務(wù),有空閑線程可用則會立即執(zhí)行,否則創(chuàng)建一個(gè)新的線程??臻e線程存活60s。
Executors.newFixedThreadPool(n):構(gòu)建具有固定大小的線程池,若任務(wù)多余空閑線程數(shù),則將多余任務(wù)放置等待隊(duì)列中。
Executors.newSingleThreadExecutor():只有一個(gè)空閑線程的線程池,任務(wù)執(zhí)行一個(gè)接一個(gè);
以上三個(gè)方法返回ExecutorService接口的ThreadPoolExecutor對象。
核心(簡介):
ThreadPoolExecutor
public class ThreadPoolExecutor extends AbstractExecutorService { public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueueworkQueue); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue workQueue,ThreadFactory threadFactory); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue workQueue,RejectedExecutionHandler handler); //事實(shí)上,前面三個(gè)構(gòu)造器都是調(diào)用的第四個(gè)構(gòu)造器進(jìn)行的初始化工作。 public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler); }
參數(shù)含義:
corePoolSize:核心池的大小,這個(gè)參數(shù)跟后面講述的線程池的實(shí)現(xiàn)原理有非常大的關(guān)系。在創(chuàng)建了線程池后,默認(rèn)情況下,線程池中并沒有任何線程,而是等待有任務(wù)到來才創(chuàng)建線程去執(zhí)行任務(wù),除非調(diào)用了prestartAllCoreThreads()或者prestartCoreThread()方法。從這2個(gè)方法的名字就可以看出,是預(yù)創(chuàng)建線程的意思,即在沒有任務(wù)到來之前就創(chuàng)建corePoolSize個(gè)線程或者一個(gè)線程。默認(rèn)情況下,在創(chuàng)建了線程池后,線程池中的線程數(shù)為0。當(dāng)有任務(wù)來之后,就會創(chuàng)建一個(gè)線程去執(zhí)行任務(wù),當(dāng)線程池中的線程數(shù)目達(dá)到corePoolSize后,就會把到達(dá)的任務(wù)放到緩存隊(duì)列當(dāng)中;
maximumPoolSize:線程池最大線程數(shù),這個(gè)參數(shù)也是一個(gè)非常重要的參數(shù),它表示在線程池中最多能創(chuàng)建多少個(gè)線程;
keepAliveTime:表示線程沒有任務(wù)執(zhí)行時(shí)最多保持多久時(shí)間會終止。默認(rèn)情況下,只有當(dāng)線程池中的線程數(shù)大于corePoolSize時(shí),keepAliveTime才會起作用。直到線程池中的線程數(shù)不大于corePoolSize,即當(dāng)線程池中的線程數(shù)大于corePoolSize時(shí),如果一個(gè)線程空閑的時(shí)間達(dá)到keepAliveTime,則會終止,直到線程池中的線程數(shù)不超過corePoolSize。但是如果調(diào)用了allowCoreThreadTimeOut(boolean)方法,在線程池中的線程數(shù)不大于corePoolSize時(shí),keepAliveTime參數(shù)也會起作用,直到線程池中的線程數(shù)為0;
unit:參數(shù)keepAliveTime的時(shí)間單位
workQueue:一個(gè)阻塞隊(duì)列,用來存儲等待執(zhí)行的任務(wù),這個(gè)參數(shù)的選擇也很重要,會對線程池的運(yùn)行過程產(chǎn)生重大影響,一般來說,這里的阻塞隊(duì)列有以下幾種選擇:
- ArrayBlockingQueue - LinkedBlockingQueue - SynchronousQueue;
threadFactory:線程工廠,主要用來創(chuàng)建線程;
handler:表示當(dāng)拒絕處理任務(wù)時(shí)的策略,有以下四種取值:
- ThreadPoolExecutor.AbortPolicy:丟棄任務(wù)并拋出RejectedExecutionException異常。 - ThreadPoolExecutor.DiscardPolicy:也是丟棄任務(wù),但是不拋出異常。 - ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊(duì)列最前面的任務(wù),然后重新嘗試執(zhí)行任務(wù)(重復(fù)此過程) - ThreadPoolExecutor.CallerRunsPolicy:由調(diào)用線程處理該任務(wù)
使用連接池應(yīng)該做的事:
1. 調(diào)用Executors的靜態(tài)方法創(chuàng)建線程池。 2. 調(diào)用submit(),提交一個(gè)Callable對象或者Runnable對象。 3. 保存好獲得的Future<>對象,其中是計(jì)算結(jié)果(如果提交的是Callable)。 4. 沒有任何任務(wù)再提交的時(shí)候使用shutDown()。
public class TaskExercise { public static void main(String[] args) throws ExecutionException, InterruptedException { //future + callable ExecutorService executor = Executors.newCachedThreadPool(); CallTask callTask = new CallTask(); Future【控制任務(wù)組taskReturn = executor.submit(callTask); System.out.println(taskReturn.get()); //futureTask FutureTask futureTask = new FutureTask (callTask); executor.submit(futureTask); System.out.println(futureTask.get()); //runable Runnable runTask = new RunTask(); String result = "xz"; Future> runTaskReturn = executor.submit(runTask,result); System.out.println(runTaskReturn.get()); executor.shutdown(); } } class CallTask implements Callable { @Override public String call() throws Exception { return "task return"; } } class RunTask implements Runnable{ @Override public void run() { System.out.println("run"); } }
有時(shí)使用執(zhí)行器更有意義的場景是控制一組相關(guān)任務(wù)。
1. shutdownNow():取消所有任務(wù) 2. invokeAny():提交所有對象到一個(gè)Callable對象集合,并返回某個(gè)已完成的任務(wù)結(jié)果。例如:如果你愿意接受任何一種解決方案的話。 3. 還有其他一些方法等我們真正用到時(shí)再學(xué)習(xí)好了~【補(bǔ)充
spring中實(shí)現(xiàn)異步調(diào)用:spring為任務(wù)調(diào)度與異步方法執(zhí)行提供了注解支持。通過在方法上設(shè)置@Async注解,可使得方法被異步調(diào)用。也就是說調(diào)用者會在調(diào)用時(shí)立即返回,而被調(diào)用方法的實(shí)際執(zhí)行是交給Spring的TaskExecutor來完成。如果是有返回值的話記得:接口返回Future<>,實(shí)現(xiàn)返回AsyncResult<>。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/70902.html
摘要:創(chuàng)建線程的方式方式一將類聲明為的子類。將該線程標(biāo)記為守護(hù)線程或用戶線程。其中方法隱含的線程為父線程?;謴?fù)線程,已過時(shí)。等待該線程銷毀終止。更多的使當(dāng)前線程在鎖存器倒計(jì)數(shù)至零之前一直等待,除非線 知識體系圖: showImg(https://segmentfault.com/img/bVbef6v?w=1280&h=960); 1、線程是什么? 線程是進(jìn)程中獨(dú)立運(yùn)行的子任務(wù)。 2、創(chuàng)建線...
摘要:某些編程語言被設(shè)計(jì)為可以將并發(fā)任務(wù)彼此隔離,這些語言通常被稱為函數(shù)性語言。通過使用多線程機(jī)制,這些獨(dú)立任務(wù)也被稱為子任務(wù)中的每一個(gè)都將由執(zhí)行線程來驅(qū)動。 并發(fā) 之前學(xué)的都是順序編程的知識,學(xué)習(xí)并發(fā)編程就好像進(jìn)入了一個(gè)全新的領(lǐng)域,有點(diǎn)類似于學(xué)習(xí)了一門新的編程語言,或者至少是學(xué)習(xí)了一整套新的語言概念。要理解并發(fā)編程,其難度與理解面向?qū)ο缶幊滩畈欢唷H绻c(diǎn)兒功夫,就能明白其基本機(jī)制,但想要...
摘要:對象改變條件對象當(dāng)前線程要等待線程終止之后才能從返回。如果線程在上的操作中被中斷,通道會被關(guān)閉,線程的中斷狀態(tài)會被設(shè)置,并得到一個(gè)。清除線程的中斷狀態(tài)。非公平性鎖雖然可能造成饑餓,但極少的線程切換,保證其更大的吞吐量。 聲明:Java并發(fā)的內(nèi)容是自己閱讀《Java并發(fā)編程實(shí)戰(zhàn)》和《Java并發(fā)編程的藝術(shù)》整理來的。 showImg(https://segmentfault.com/im...
摘要:相比與其他操作系統(tǒng)包括其他類系統(tǒng)有很多的優(yōu)點(diǎn),其中有一項(xiàng)就是,其上下文切換和模式切換的時(shí)間消耗非常少。因?yàn)槎嗑€程競爭鎖時(shí)會引起上下文切換。減少線程的使用。很多編程語言中都有協(xié)程。所以如何避免死鎖的產(chǎn)生,在我們使用并發(fā)編程時(shí)至關(guān)重要。 系列文章傳送門: Java多線程學(xué)習(xí)(一)Java多線程入門 Java多線程學(xué)習(xí)(二)synchronized關(guān)鍵字(1) java多線程學(xué)習(xí)(二)syn...
摘要:因?yàn)槎嗑€程競爭鎖時(shí)會引起上下文切換。減少線程的使用。舉個(gè)例子如果說服務(wù)器的帶寬只有,某個(gè)資源的下載速度是,系統(tǒng)啟動個(gè)線程下載該資源并不會導(dǎo)致下載速度編程,所以在并發(fā)編程時(shí),需要考慮這些資源的限制。 最近私下做一項(xiàng)目,一bug幾日未解決,總惶恐。一日頓悟,bug不可怕,怕的是項(xiàng)目不存在bug,與其懼怕,何不與其剛正面。 系列文章傳送門: Java多線程學(xué)習(xí)(一)Java多線程入門 Jav...
閱讀 1853·2023-04-26 02:14
閱讀 3775·2021-11-23 09:51
閱讀 1420·2021-10-13 09:39
閱讀 4003·2021-09-24 10:36
閱讀 3037·2021-09-22 15:55
閱讀 3542·2019-08-30 12:57
閱讀 2060·2019-08-29 15:30
閱讀 2011·2019-08-29 13:19