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