摘要:一使用線程池的好處線程池提供了一種限制和管理資源包括執(zhí)行一個任務(wù)。每個線程池還維護一些基本統(tǒng)計信息,例如已完成任務(wù)的數(shù)量。通過重復利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗。使用無界隊列作為線程池的工作隊列會對線程池帶來的影響與相同。
歷史優(yōu)質(zhì)文章推薦:
Java并發(fā)編程指南專欄
分布式系統(tǒng)的經(jīng)典基礎(chǔ)理論
可能是最漂亮的Spring事務(wù)管理詳解
面試中關(guān)于Java虛擬機(jvm)的問題看這篇就夠了
目錄:[TOC]
本節(jié)思維導圖:
思維導圖源文件+思維導圖軟件關(guān)注微信公眾號:“Java面試通關(guān)手冊” 回復關(guān)鍵字:“Java多線程” 免費領(lǐng)取。
一 使用線程池的好處線程池提供了一種限制和管理資源(包括執(zhí)行一個任務(wù))。 每個線程池還維護一些基本統(tǒng)計信息,例如已完成任務(wù)的數(shù)量。
這里借用《Java并發(fā)編程的藝術(shù)》提到的來說一下使用線程池的好處:
降低資源消耗。通過重復利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗。
提高響應(yīng)速度。當任務(wù)到達時,任務(wù)可以不需要的等到線程創(chuàng)建就能立即執(zhí)行。
提高線程的可管理性。線程是稀缺資源,如果無限制的創(chuàng)建,不僅會消耗系統(tǒng)資源,還會降低系統(tǒng)的穩(wěn)定性,使用線程池可以進行統(tǒng)一的分配,調(diào)優(yōu)和監(jiān)控。
二 Executor 框架 2.1 簡介Executor 框架是Java5之后引進的,在Java 5之后,通過 Executor 來啟動線程比使用 Thread 的 start 方法更好,除了更易管理,效率更好(用線程池實現(xiàn),節(jié)約開銷)外,還有關(guān)鍵的一點:有助于避免 this 逃逸問題。
補充:this逃逸是指在構(gòu)造函數(shù)返回之前其他線程就持有該對象的引用. 調(diào)用尚未構(gòu)造完全的對象的方法可能引發(fā)令人疑惑的錯誤。2.2 Executor 框架結(jié)構(gòu)(主要由三大部分組成) 1 任務(wù)。
執(zhí)行任務(wù)需要實現(xiàn)的Runnable接口或Callable接口。
Runnable接口或Callable接口實現(xiàn)類都可以被ThreadPoolExecutor或ScheduledThreadPoolExecutor執(zhí)行。
兩者的區(qū)別:
Runnable接口不會返回結(jié)果但是Callable接口可以返回結(jié)果。后面介紹Executors類的一些方法的時候會介紹到兩者的相互轉(zhuǎn)換。2 任務(wù)的執(zhí)行
如下圖所示,包括任務(wù)執(zhí)行機制的核心接口Executor ,以及繼承自Executor 接口的ExecutorService接口。ScheduledThreadPoolExecutor和ThreadPoolExecutor這兩個關(guān)鍵類實現(xiàn)了ExecutorService接口。
注意: 通過查看ScheduledThreadPoolExecutor源代碼我們發(fā)現(xiàn)ScheduledThreadPoolExecutor實際上是繼承了ThreadPoolExecutor并實現(xiàn)了ScheduledExecutorService ,而ScheduledExecutorService又實現(xiàn)了ExecutorService,正如我們下面給出的類關(guān)系圖顯示的一樣。
ThreadPoolExecutor類描述:
//AbstractExecutorService實現(xiàn)了ExecutorService接口 public class ThreadPoolExecutor extends AbstractExecutorService
ScheduledThreadPoolExecutor類描述:
//ScheduledExecutorService實現(xiàn)了ExecutorService接口 public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService3 異步計算的結(jié)果
Future接口以及Future接口的實現(xiàn)類FutureTask類。
當我們把Runnable接口或Callable接口的實現(xiàn)類提交(調(diào)用submit方法)給ThreadPoolExecutor或ScheduledThreadPoolExecutor時,會返回一個FutureTask對象。
我們以AbstractExecutorService接口中的一個submit方法為例子來看看源代碼:
public Future> submit(Runnable task) { if (task == null) throw new NullPointerException(); RunnableFutureftask = newTaskFor(task, null); execute(ftask); return ftask; }
上面方法調(diào)用的newTaskFor方法返回了一個FutureTask對象。
protected2.3 Executor 框架的使用示意圖RunnableFuture newTaskFor(Runnable runnable, T value) { return new FutureTask (runnable, value); }
主線程首先要創(chuàng)建實現(xiàn)Runnable或者Callable接口的任務(wù)對象。
備注: 工具類Executors可以實現(xiàn)Runnable對象和Callable對象之間的相互轉(zhuǎn)換。(Executors.callable(Runnable task)或Executors.callable(Runnable task,Object resule))。
然后可以把創(chuàng)建完成的Runnable對象直接交給ExecutorService執(zhí)行(ExecutorService.execute(Runnable command));或者也可以把Runnable對象或Callable對象提交給ExecutorService執(zhí)行(ExecutorService.submit(Runnable task)或ExecutorService.submit(Callable
執(zhí)行execute()方法和submit()方法的區(qū)別是什么呢?
1)execute()方法用于提交不需要返回值的任務(wù),所以無法判斷任務(wù)是否被線程池執(zhí)行成功與否;
2)submit()方法用于提交需要返回值的任務(wù)。線程池會返回一個future類型的對象,通過這個future對象可以判斷任務(wù)是否執(zhí)行成功,并且可以通過future的get()方法來獲取返回值,get()方法會阻塞當前線程直到任務(wù)完成,而使用get(long timeout,TimeUnit unit)方法則會阻塞當前線程一段時間后立即返回,這時候有可能任務(wù)沒有執(zhí)行完。
如果執(zhí)行ExecutorService.submit(…),ExecutorService將返回一個實現(xiàn)Future接口的對象(我們剛剛也提到過了執(zhí)行execute()方法和submit()方法的區(qū)別,到目前為止的JDK中,返回的是FutureTask對象)。由于FutureTask實現(xiàn)了Runnable,程序員也可以創(chuàng)建FutureTask,然后直接交給ExecutorService執(zhí)行。
最后,主線程可以執(zhí)行FutureTask.get()方法來等待任務(wù)執(zhí)行完成。主線程也可以執(zhí)行FutureTask.cancel(boolean mayInterruptIfRunning)來取消此任務(wù)的執(zhí)行。
三 ThreadPoolExecutor詳解線程池實現(xiàn)類ThreadPoolExecutor是Executor 框架最核心的類,先來看一下這個類中比較重要的四個屬性
3.1 ThreadPoolExecutor類的四個比較重要的屬性 3.2 ThreadPoolExecutor類中提供的四個構(gòu)造方法我們看最長的那個,其余三個都是在這個構(gòu)造方法的基礎(chǔ)上產(chǎn)生(給定某些默認參數(shù)的構(gòu)造方法)
/** * 用給定的初始參數(shù)創(chuàng)建一個新的ThreadPoolExecutor。 * @param keepAliveTime 當線程池中的線程數(shù)量大于corePoolSize的時候,如果這時沒有新的任務(wù)提交, *核心線程外的線程不會立即銷毀,而是會等待,直到等待的時間超過了keepAliveTime; * @param unit keepAliveTime參數(shù)的時間單位 * @param workQueue 等待隊列,當任務(wù)提交時,如果線程池中的線程數(shù)量大于等于corePoolSize的時候,把該任務(wù)封裝成一個Worker對象放入等待隊列; * * @param threadFactory 執(zhí)行者創(chuàng)建新線程時使用的工廠 * @param handler RejectedExecutionHandler類型的變量,表示線程池的飽和策略。 * 如果阻塞隊列滿了并且沒有空閑的線程,這時如果繼續(xù)提交任務(wù),就需要采取一種策略處理該任務(wù)。 * 線程池提供了4種策略: 1.AbortPolicy:直接拋出異常,這是默認策略; 2.CallerRunsPolicy:用調(diào)用者所在的線程來執(zhí)行任務(wù); 3.DiscardOldestPolicy:丟棄阻塞隊列中靠最前的任務(wù),并執(zhí)行當前任務(wù); 4.DiscardPolicy:直接丟棄任務(wù); */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue3.3 如何創(chuàng)建ThreadPoolExecutorworkQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
方式一:通過構(gòu)造方法實現(xiàn)(官方API文檔并不推薦,所以建議使用第二種方式)
方式二:通過Executor 框架的工具類Executors來實現(xiàn)
我們可以創(chuàng)建三種類型的ThreadPoolExecutor:
FixedThreadPool
SingleThreadExecutor
CachedThreadPool
對應(yīng)Executors工具類中的方法如圖所示:
FixedThreadPool被稱為可重用固定線程數(shù)的線程池。通過Executors類中的相關(guān)源代碼來看一下相關(guān)實現(xiàn):
/** * 創(chuàng)建一個可重用固定數(shù)量線程的線程池 *在任何時候至多有n個線程處于活動狀態(tài) *如果在所有線程處于活動狀態(tài)時提交其他任務(wù),則它們將在隊列中等待, *直到線程可用。 如果任何線程在關(guān)閉之前的執(zhí)行期間由于失敗而終止, *如果需要執(zhí)行后續(xù)任務(wù),則一個新的線程將取代它。池中的線程將一直存在 *知道調(diào)用shutdown方法 * @param nThreads 線程池中的線程數(shù) * @param threadFactory 創(chuàng)建新線程時使用的factory * @return 新創(chuàng)建的線程池 * @throws NullPointerException 如果threadFactory為null * @throws IllegalArgumentException if {@code nThreads <= 0} */ public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), threadFactory); }
另外還有一個FixedThreadPool的實現(xiàn)方法,和上面的類似,所以這里不多做闡述:
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); }
從上面源代碼可以看出新創(chuàng)建的FixedThreadPool的corePoolSize和maximumPoolSize都被設(shè)置為nThreads。
FixedThreadPool的execute()方法運行示意圖(該圖片來源:《Java并發(fā)編程的藝術(shù)》):
上圖說明:
如果當前運行的線程數(shù)小于corePoolSize,則創(chuàng)建新的線程來執(zhí)行任務(wù);
當前運行的線程數(shù)等于corePoolSize后,將任務(wù)加入LinkedBlockingQueue;
線程執(zhí)行完1中的任務(wù)后,會在循環(huán)中反復從LinkedBlockingQueue中獲取任務(wù)來執(zhí)行;
FixedThreadPool使用無界隊列 LinkedBlockingQueue(隊列的容量為Intger.MAX_VALUE)作為線程池的工作隊列會對線程池帶來如下影響:
當線程池中的線程數(shù)達到corePoolSize后,新任務(wù)將在無界隊列中等待,因此線程池中的線程數(shù)不會超過corePoolSize;
由于1,使用無界隊列時maximumPoolSize將是一個無效參數(shù);
由于1和2,使用無界隊列時keepAliveTime將是一個無效參數(shù);
運行中的FixedThreadPool(未執(zhí)行shutdown()或shutdownNow()方法)不會拒絕任務(wù)
3.5 SingleThreadExecutor詳解SingleThreadExecutor是使用單個worker線程的Executor。下面看看SingleThreadExecutor的實現(xiàn):
/** *創(chuàng)建使用單個worker線程運行無界隊列的Executor *并使用提供的ThreadFactory在需要時創(chuàng)建新線程 * * @param threadFactory 創(chuàng)建新線程時使用的factory * * @return 新創(chuàng)建的單線程Executor * @throws NullPointerException 如果ThreadFactory為空 */ public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), threadFactory)); }
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue())); }
從上面源代碼可以看出新創(chuàng)建的SingleThreadExecutor的corePoolSize和maximumPoolSize都被設(shè)置為1.其他參數(shù)和FixedThreadPool相同。SingleThreadExecutor使用無界隊列LinkedBlockingQueue作為線程池的工作隊列(隊列的容量為Intger.MAX_VALUE)。SingleThreadExecutor使用無界隊列作為線程池的工作隊列會對線程池帶來的影響與FixedThreadPool相同。
SingleThreadExecutor的運行示意圖(該圖片來源:《Java并發(fā)編程的藝術(shù)》):
上圖說明;
如果當前運行的線程數(shù)少于corePoolSize,則創(chuàng)建一個新的線程執(zhí)行任務(wù);
當前線程池中有一個運行的線程后,將任務(wù)加入LinkedBlockingQueue
線程執(zhí)行完1中的任務(wù)后,會在循環(huán)中反復從LinkedBlockingQueue中獲取任務(wù)來執(zhí)行;
3.6 CachedThreadPool詳解CachedThreadPool是一個會根據(jù)需要創(chuàng)建新線程的線程池。下面通過源碼來看看 CachedThreadPool的實現(xiàn):
/** * 創(chuàng)建一個線程池,根據(jù)需要創(chuàng)建新線程,但會在先前構(gòu)建的線程可用時重用它, *并在需要時使用提供的ThreadFactory創(chuàng)建新線程。 * @param threadFactory 創(chuàng)建新線程使用的factory * @return 新創(chuàng)建的線程池 * @throws NullPointerException 如果threadFactory為空 */ public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue(), threadFactory); }
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue()); }
CachedThreadPool的corePoolSize被設(shè)置為空(0),maximumPoolSize被設(shè)置為Integer.MAX.VALUE,即它是無界的,這也就意味著如果主線程提交任務(wù)的速度高于maximumPool中線程處理任務(wù)的速度時,CachedThreadPool會不斷創(chuàng)建新的線程。極端情況下,這樣會導致耗盡cpu和內(nèi)存資源。
CachedThreadPool的execute()方法的執(zhí)行示意圖(該圖片來源:《Java并發(fā)編程的藝術(shù)》):
上圖說明:
首先執(zhí)行SynchronousQueue.offer(Runnable task)。如果當前maximumPool中有閑線程正在執(zhí)行SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS),那么主線程執(zhí)行offer操作與空閑線程執(zhí)行的poll操作配對成功,主線程把任務(wù)交給空閑線程執(zhí)行,execute()方法執(zhí)行完成,否則執(zhí)行下面的步驟2;
當初始maximumPool為空,或者maximumPool中沒有空閑線程時,將沒有線程執(zhí)行SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS)。這種情況下,步驟1將失敗,此時CachedThreadPool會創(chuàng)建新線程執(zhí)行任務(wù),execute方法執(zhí)行完成;
3.7 ThreadPoolExecutor使用示例 3.7.1 示例代碼首先創(chuàng)建一個Runnable接口的實現(xiàn)類(當然也可以是Callable接口,我們上面也說了兩者的區(qū)別是:Runnable接口不會返回結(jié)果但是Callable接口可以返回結(jié)果。后面介紹Executors類的一些方法的時候會介紹到兩者的相互轉(zhuǎn)換。)
import java.util.Date; /** * 這是一個簡單的Runnable類,需要大約5秒鐘來執(zhí)行其任務(wù)。 */ public class WorkerThread implements Runnable { private String command; public WorkerThread(String s) { this.command = s; } @Override public void run() { System.out.println(Thread.currentThread().getName() + " Start. Time = " + new Date()); processCommand(); System.out.println(Thread.currentThread().getName() + " End. Time = " + new Date()); } private void processCommand() { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public String toString() { return this.command; } }
編寫測試程序,我們這里以FixedThreadPool為例子
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolExecutorDemo { public static void main(String[] args) { //創(chuàng)建一個FixedThreadPool對象 ExecutorService executor = Executors.newFixedThreadPool(5); for (int i = 0; i < 10; i++) { //創(chuàng)建WorkerThread對象(WorkerThread類實現(xiàn)了Runnable 接口) Runnable worker = new WorkerThread("" + i); //執(zhí)行Runnable executor.execute(worker); } //終止線程池 executor.shutdown(); while (!executor.isTerminated()) { } System.out.println("Finished all threads"); } }
輸出示例:
pool-1-thread-5 Start. Time = Thu May 31 10:22:52 CST 2018 pool-1-thread-3 Start. Time = Thu May 31 10:22:52 CST 2018 pool-1-thread-2 Start. Time = Thu May 31 10:22:52 CST 2018 pool-1-thread-4 Start. Time = Thu May 31 10:22:52 CST 2018 pool-1-thread-1 Start. Time = Thu May 31 10:22:52 CST 2018 pool-1-thread-4 End. Time = Thu May 31 10:22:57 CST 2018 pool-1-thread-1 End. Time = Thu May 31 10:22:57 CST 2018 pool-1-thread-2 End. Time = Thu May 31 10:22:57 CST 2018 pool-1-thread-5 End. Time = Thu May 31 10:22:57 CST 2018 pool-1-thread-3 End. Time = Thu May 31 10:22:57 CST 2018 pool-1-thread-5 Start. Time = Thu May 31 10:22:57 CST 2018 pool-1-thread-2 Start. Time = Thu May 31 10:22:57 CST 2018 pool-1-thread-1 Start. Time = Thu May 31 10:22:57 CST 2018 pool-1-thread-4 Start. Time = Thu May 31 10:22:57 CST 2018 pool-1-thread-3 Start. Time = Thu May 31 10:22:57 CST 2018 pool-1-thread-5 End. Time = Thu May 31 10:23:02 CST 2018 pool-1-thread-1 End. Time = Thu May 31 10:23:02 CST 2018 pool-1-thread-2 End. Time = Thu May 31 10:23:02 CST 2018 pool-1-thread-3 End. Time = Thu May 31 10:23:02 CST 2018 pool-1-thread-4 End. Time = Thu May 31 10:23:02 CST 2018 Finished all threads3.7.2 shutdown()VS shutdownNow()
shutdown()方法表明關(guān)閉已在Executor上調(diào)用,因此不會再向DelayedPool添加任何其他任務(wù)(由ScheduledThreadPoolExecutor類在內(nèi)部使用)。 但是,已經(jīng)在隊列中提交的任務(wù)將被允許完成。
另一方面,shutdownNow()方法試圖終止當前正在運行的任務(wù),并停止處理排隊的任務(wù)并返回正在等待執(zhí)行的List。
isShutdown()表示執(zhí)行程序正在關(guān)閉,但并非所有任務(wù)都已完成執(zhí)行。
另一方面,isShutdown()表示所有線程都已完成執(zhí)行。
ScheduledThreadPoolExecutor主要用來在給定的延遲后運行任務(wù),或者定期執(zhí)行任務(wù)。
ScheduledThreadPoolExecutor使用的任務(wù)隊列DelayQueue封裝了一個PriorityQueue,PriorityQueue會對隊列中的任務(wù)進行排序,執(zhí)行所需時間短的放在前面先被執(zhí)行(ScheduledFutureTask的time變量小的先執(zhí)行),如果執(zhí)行所需時間相同則先提交的任務(wù)將被先執(zhí)行(ScheduledFutureTask的squenceNumber變量小的先執(zhí)行)。
ScheduledThreadPoolExecutor和Timer的比較:
Timer對系統(tǒng)時鐘的變化敏感,ScheduledThreadPoolExecutor不是;
Timer只有一個執(zhí)行線程,因此長時間運行的任務(wù)可以延遲其他任務(wù)。 ScheduledThreadPoolExecutor可以配置任意數(shù)量的線程。 此外,如果你想(通過提供ThreadFactory),你可以完全控制創(chuàng)建的線程;
在TimerTask中拋出的運行時異常會殺死一個線程,從而導致Timer死機:-( ...即計劃任務(wù)將不再運行。ScheduledThreadExecutor不僅捕獲運行時異常,還允許您在需要時處理它們(通過重寫afterExecute方法 ThreadPoolExecutor)。拋出異常的任務(wù)將被取消,但其他任務(wù)將繼續(xù)運行。
綜上,在JDK1.5之后,你沒有理由再使用Timer進行任務(wù)調(diào)度了。
備注: Quartz是一個由java編寫的任務(wù)調(diào)度庫,由OpenSymphony組織開源出來。在實際項目開發(fā)中使用Quartz的還是居多,比較推薦使用Quartz。因為Quartz理論上能夠同時對上萬個任務(wù)進行調(diào)度,擁有豐富的功能特性,包括任務(wù)調(diào)度、任務(wù)持久化、可集群化、插件等等。4.2 ScheduledThreadPoolExecutor運行機制
ScheduledThreadPoolExecutor的執(zhí)行主要分為兩大部分:
當調(diào)用ScheduledThreadPoolExecutor的 scheduleAtFixedRate() 方法或者scheduleWirhFixedDelay() 方法時,會向ScheduledThreadPoolExecutor的 DelayQueue 添加一個實現(xiàn)了 RunnableScheduledFutur 接口的 ScheduledFutureTask 。
線程池中的線程從DelayQueue中獲取ScheduledFutureTask,然后執(zhí)行任務(wù)。
ScheduledThreadPoolExecutor為了實現(xiàn)周期性的執(zhí)行任務(wù),對ThreadPoolExecutor做了如下修改:
使用 DelayQueue 作為任務(wù)隊列;
獲取任務(wù)的方不同
執(zhí)行周期任務(wù)后,增加了額外的處理
4.3 ScheduledThreadPoolExecutor執(zhí)行周期任務(wù)的步驟線程1從DelayQueue中獲取已到期的ScheduledFutureTask(DelayQueue.take())。到期任務(wù)是指ScheduledFutureTask的time大于等于當前系統(tǒng)的時間;
線程1執(zhí)行這個ScheduledFutureTask;
線程1修改ScheduledFutureTask的time變量為下次將要被執(zhí)行的時間;
線程1把這個修改time之后的ScheduledFutureTask放回DelayQueue中(DelayQueue.add())。
4.4 ScheduledThreadPoolExecutor使用示例創(chuàng)建一個簡單的實現(xiàn)Runnable接口的類(我們上面的例子已經(jīng)實現(xiàn)過)
測試程序使用ScheduledExecutorService和ScheduledThreadPoolExecutor實現(xiàn)的java調(diào)度。
/** * 使用ScheduledExecutorService和ScheduledThreadPoolExecutor實現(xiàn)的java調(diào)度程序示例程序。 */ public class ScheduledThreadPoolDemo { public static void main(String[] args) throws InterruptedException { //創(chuàng)建一個ScheduledThreadPoolExecutor對象 ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5); //計劃在某段時間后運行 System.out.println("Current Time = "+new Date()); for(int i=0; i<3; i++){ Thread.sleep(1000); WorkerThread worker = new WorkerThread("do heavy processing"); //創(chuàng)建并執(zhí)行在給定延遲后啟用的單次操作。 scheduledThreadPool.schedule(worker, 10, TimeUnit.SECONDS); } //添加一些延遲讓調(diào)度程序產(chǎn)生一些線程 Thread.sleep(30000); System.out.println("Current Time = "+new Date()); //關(guān)閉線程池 scheduledThreadPool.shutdown(); while(!scheduledThreadPool.isTerminated()){ //等待所有任務(wù)完成 } System.out.println("Finished all threads"); } }
運行結(jié)果:
Current Time = Wed May 30 17:11:16 CST 2018 pool-1-thread-1 Start. Time = Wed May 30 17:11:27 CST 2018 pool-1-thread-2 Start. Time = Wed May 30 17:11:28 CST 2018 pool-1-thread-3 Start. Time = Wed May 30 17:11:29 CST 2018 pool-1-thread-1 End. Time = Wed May 30 17:11:32 CST 2018 pool-1-thread-2 End. Time = Wed May 30 17:11:33 CST 2018 pool-1-thread-3 End. Time = Wed May 30 17:11:34 CST 2018 Current Time = Wed May 30 17:11:49 CST 2018 Finished all threads4.4.1 ScheduledExecutorService scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit)方法
我們可以使用ScheduledExecutorService scheduleAtFixedRate方法來安排任務(wù)在初始延遲后運行,然后在給定的時間段內(nèi)運行。
時間段是從池中第一個線程的開始,因此如果您將period指定為1秒并且線程運行5秒,那么只要第一個工作線程完成執(zhí)行,下一個線程就會開始執(zhí)行。
for (int i = 0; i < 3; i++) { Thread.sleep(1000); WorkerThread worker = new WorkerThread("do heavy processing"); // schedule task to execute at fixed rate scheduledThreadPool.scheduleAtFixedRate(worker, 0, 10, TimeUnit.SECONDS); }
輸出示例:
Current Time = Wed May 30 17:47:09 CST 2018 pool-1-thread-1 Start. Time = Wed May 30 17:47:10 CST 2018 pool-1-thread-2 Start. Time = Wed May 30 17:47:11 CST 2018 pool-1-thread-3 Start. Time = Wed May 30 17:47:12 CST 2018 pool-1-thread-1 End. Time = Wed May 30 17:47:15 CST 2018 pool-1-thread-2 End. Time = Wed May 30 17:47:16 CST 2018 pool-1-thread-3 End. Time = Wed May 30 17:47:17 CST 2018 pool-1-thread-1 Start. Time = Wed May 30 17:47:20 CST 2018 pool-1-thread-4 Start. Time = Wed May 30 17:47:21 CST 2018 pool-1-thread-2 Start. Time = Wed May 30 17:47:22 CST 2018 pool-1-thread-1 End. Time = Wed May 30 17:47:25 CST 2018 pool-1-thread-4 End. Time = Wed May 30 17:47:26 CST 2018 pool-1-thread-2 End. Time = Wed May 30 17:47:27 CST 2018 pool-1-thread-1 Start. Time = Wed May 30 17:47:30 CST 2018 pool-1-thread-3 Start. Time = Wed May 30 17:47:31 CST 2018 pool-1-thread-5 Start. Time = Wed May 30 17:47:32 CST 2018 pool-1-thread-1 End. Time = Wed May 30 17:47:35 CST 2018 pool-1-thread-3 End. Time = Wed May 30 17:47:36 CST 2018 pool-1-thread-5 End. Time = Wed May 30 17:47:37 CST 2018 pool-1-thread-1 Start. Time = Wed May 30 17:47:40 CST 2018 pool-1-thread-2 Start. Time = Wed May 30 17:47:41 CST 2018 Current Time = Wed May 30 17:47:42 CST 2018 pool-1-thread-1 End. Time = Wed May 30 17:47:45 CST 2018 pool-1-thread-2 End. Time = Wed May 30 17:47:46 CST 2018 Finished all threads Process finished with exit code 04.4.2 ScheduledExecutorService scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit)方法
ScheduledExecutorService scheduleWithFixedDelay方法可用于以初始延遲啟動周期性執(zhí)行,然后以給定延遲執(zhí)行。 延遲時間是線程完成執(zhí)行的時間。
for (int i = 0; i < 3; i++) { Thread.sleep(1000); WorkerThread worker = new WorkerThread("do heavy processing"); scheduledThreadPool.scheduleWithFixedDelay(worker, 0, 1, TimeUnit.SECONDS); }
輸出示例:
Current Time = Wed May 30 17:58:09 CST 2018 pool-1-thread-1 Start. Time = Wed May 30 17:58:10 CST 2018 pool-1-thread-2 Start. Time = Wed May 30 17:58:11 CST 2018 pool-1-thread-3 Start. Time = Wed May 30 17:58:12 CST 2018 pool-1-thread-1 End. Time = Wed May 30 17:58:15 CST 2018 pool-1-thread-2 End. Time = Wed May 30 17:58:16 CST 2018 pool-1-thread-1 Start. Time = Wed May 30 17:58:16 CST 2018 pool-1-thread-3 End. Time = Wed May 30 17:58:17 CST 2018 pool-1-thread-4 Start. Time = Wed May 30 17:58:17 CST 2018 pool-1-thread-2 Start. Time = Wed May 30 17:58:18 CST 2018 pool-1-thread-1 End. Time = Wed May 30 17:58:21 CST 2018 pool-1-thread-1 Start. Time = Wed May 30 17:58:22 CST 2018 pool-1-thread-4 End. Time = Wed May 30 17:58:22 CST 2018 pool-1-thread-2 End. Time = Wed May 30 17:58:23 CST 2018 pool-1-thread-2 Start. Time = Wed May 30 17:58:23 CST 2018 pool-1-thread-4 Start. Time = Wed May 30 17:58:24 CST 2018 pool-1-thread-1 End. Time = Wed May 30 17:58:27 CST 2018 pool-1-thread-2 End. Time = Wed May 30 17:58:28 CST 2018 pool-1-thread-1 Start. Time = Wed May 30 17:58:28 CST 2018 pool-1-thread-2 Start. Time = Wed May 30 17:58:29 CST 2018 pool-1-thread-4 End. Time = Wed May 30 17:58:29 CST 2018 pool-1-thread-4 Start. Time = Wed May 30 17:58:30 CST 2018 pool-1-thread-1 End. Time = Wed May 30 17:58:33 CST 2018 pool-1-thread-2 End. Time = Wed May 30 17:58:34 CST 2018 pool-1-thread-1 Start. Time = Wed May 30 17:58:34 CST 2018 pool-1-thread-2 Start. Time = Wed May 30 17:58:35 CST 2018 pool-1-thread-4 End. Time = Wed May 30 17:58:35 CST 2018 pool-1-thread-4 Start. Time = Wed May 30 17:58:36 CST 2018 pool-1-thread-1 End. Time = Wed May 30 17:58:39 CST 2018 pool-1-thread-2 End. Time = Wed May 30 17:58:40 CST 2018 pool-1-thread-5 Start. Time = Wed May 30 17:58:40 CST 2018 pool-1-thread-4 End. Time = Wed May 30 17:58:41 CST 2018 pool-1-thread-2 Start. Time = Wed May 30 17:58:41 CST 2018 Current Time = Wed May 30 17:58:42 CST 2018 pool-1-thread-5 End. Time = Wed May 30 17:58:45 CST 2018 pool-1-thread-2 End. Time = Wed May 30 17:58:46 CST 2018 Finished all threads4.4.3 scheduleWithFixedDelay() vs scheduleAtFixedRate()
scheduleAtFixedRate(...)將延遲視為兩個任務(wù)開始之間的差異(即定期調(diào)用)
scheduleWithFixedDelay(...)將延遲視為一個任務(wù)結(jié)束與下一個任務(wù)開始之間的差異
scheduleAtFixedRate(): 創(chuàng)建并執(zhí)行在給定的初始延遲之后,隨后以給定的時間段首先啟用的周期性動作; 那就是執(zhí)行將在initialDelay之后開始,然后initialDelay+period ,然后是initialDelay + 2 * period ,等等。 如果任務(wù)的執(zhí)行遇到異常,則后續(xù)的執(zhí)行被抑制。 否則,任務(wù)將僅通過取消或終止執(zhí)行人終止。 如果任務(wù)執(zhí)行時間比其周期長,則后續(xù)執(zhí)行可能會遲到,但不會同時執(zhí)行。五 各種線程池的適用場景介紹
scheduleWithFixedDelay() : 創(chuàng)建并執(zhí)行在給定的初始延遲之后首先啟用的定期動作,隨后在一個執(zhí)行的終止和下一個執(zhí)行的開始之間給定的延遲。 如果任務(wù)的執(zhí)行遇到異常,則后續(xù)的執(zhí)行被抑制。 否則,任務(wù)將僅通過取消或終止執(zhí)行終止。
FixedThreadPool: 適用于為了滿足資源管理需求,而需要限制當前線程數(shù)量的應(yīng)用場景。它適用于負載比較重的服務(wù)器;
SingleThreadExecutor: 適用于需要保證順序地執(zhí)行各個任務(wù)并且在任意時間點,不會有多個線程是活動的應(yīng)用場景。
CachedThreadPool: 適用于執(zhí)行很多的短期異步任務(wù)的小程序,或者是負載較輕的服務(wù)器;
ScheduledThreadPoolExecutor: 適用于需要多個后臺執(zhí)行周期任務(wù),同時為了滿足資源管理需求而需要限制后臺線程的數(shù)量的應(yīng)用場景,
SingleThreadScheduledExecutor: 適用于需要單個后臺線程執(zhí)行周期任務(wù),同時保證順序地執(zhí)行各個任務(wù)的應(yīng)用場景。
六 總結(jié)本節(jié)只是簡單的介紹了一下使用線程池的好處,然后花了大量篇幅介紹Executor 框架。詳細介紹了Executor 框架中ThreadPoolExecutor和ScheduledThreadPoolExecutor,并且通過實例詳細講解了ScheduledThreadPoolExecutor的使用。對于FutureTask 只是粗略帶過,因為篇幅問題,并沒有深究它的原理,后面的文章會進行補充。這一篇文章只是大概帶大家過一下線程池的基本概覽,深入講解的地方不是很多,后續(xù)會通過源碼深入研究其中比較重要的一些知識點。
最后,就是這兩周要考試了,會抽點時間出來簡單應(yīng)付一下學校考試了。然后,就是寫這篇多線程的文章廢了好多好多時間。一直不知從何寫起。
參考《Java并發(fā)編程的藝術(shù)》
Java Scheduler ScheduledExecutorService ScheduledThreadPoolExecutor Example
java.util.concurrent.ScheduledThreadPoolExecutor Example
ThreadPoolExecutor – Java Thread Pool Example
我是Snailclimb,一個以架構(gòu)師為5年之內(nèi)目標的小小白。 歡迎關(guān)注我的微信公眾號:"Java面試通關(guān)手冊"(一個有溫度的微信公眾號,期待與你共同進步~~~堅持原創(chuàng),分享美文,分享各種Java學習資源)
最后,就是使用阿里云服務(wù)器一段時間后,感覺阿里云真的很不錯,就申請做了阿里云大使,然后這是我的優(yōu)惠券地址.
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/69597.html
摘要:時,標準類庫添加了,作為對型線程池的實現(xiàn)。類圖用來專門定義型任務(wù)完成將大任務(wù)分割為小任務(wù)以及合并結(jié)果的工作。 JDK 1.7 時,標準類庫添加了 ForkJoinPool,作為對 Fork/Join 型線程池的實現(xiàn)。Fork 在英文中有 分叉 的意思,而 Join 有 合并 的意思。ForkJoinPool 的功能也是如此:Fork 將大任務(wù)分叉為多個小任務(wù),然后讓小任務(wù)執(zhí)行,Join...
摘要:定義給出的原型模式定義如下使用原型實例指定將要創(chuàng)建的對象類型,通過復制這個實例創(chuàng)建新的對象。具體原型類角色負責實現(xiàn)復制現(xiàn)有實例并生成新實例的方法。 Java面試通關(guān)手冊(Java學習指南,歡迎Star,會一直完善下去,歡迎建議和指導):https://github.com/Snailclimb/Java_Guide 系列文章回顧: 設(shè)計模式專欄深入理解單例模式深入理解工廠模式 深入理解...
摘要:本文主要內(nèi)容為簡單總結(jié)中線程池的相關(guān)信息。方法簇方法簇用于創(chuàng)建固定線程數(shù)的線程池。三種常見線程池的對比上文總結(jié)了工具類創(chuàng)建常見線程池的方法,現(xiàn)對三種線程池區(qū)別進行比較。 概述 線程可認為是操作系統(tǒng)可調(diào)度的最小的程序執(zhí)行序列,一般作為進程的組成部分,同一進程中多個線程可共享該進程的資源(如內(nèi)存等)。在單核處理器架構(gòu)下,操作系統(tǒng)一般使用分時的方式實現(xiàn)多線程;在多核處理器架構(gòu)下,多個線程能夠...
摘要:接私活對程序員這個圈子來說是一個既公開又隱私的話題,不說全部,應(yīng)該大多數(shù)程序員都有過想要接私活的想法,當然,也有部分得道成仙的不主張接私活。 接私活 對程序員這個圈子來說是一個既公開又隱私的話題,不說全部,應(yīng)該大多數(shù)程序員都有過想要接私活的想法,當然,也有部分得道成仙的不主張接私活。但是很少...
摘要:命令和創(chuàng)建快照原理十分相似,所以文件重寫也需要用到子進程,這樣會導致性能問題和內(nèi)存占用問題,和快照持久化一樣。 歷史文章推薦: 一只準程序猿的嘮叨 可能是最漂亮的Spring事務(wù)管理詳解 Java多線程學習(八)線程池與Executor 框架 面試中關(guān)于Redis的問題看這篇就夠了 非常感謝《redis實戰(zhàn)》真本書,本文大多內(nèi)容也參考了書中的內(nèi)容。非常推薦大家看一下《redis實戰(zhàn)》這...
閱讀 3040·2023-04-26 00:49
閱讀 3719·2021-09-29 09:45
閱讀 964·2019-08-29 18:47
閱讀 2738·2019-08-29 18:37
閱讀 2723·2019-08-29 16:37
閱讀 3286·2019-08-29 13:24
閱讀 1773·2019-08-27 10:56
閱讀 2344·2019-08-26 11:42