Executors
1.概述
初學Java多線程,常使用Thread與Runnable創(chuàng)建、啟動線程。如下例:
Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread().getName());} }); t1.start();我們需要自己創(chuàng)建、啟動Thread對象。
2. 使用Executors執(zhí)行線程
一些已有的執(zhí)行器可以幫我們管理Thread對象。你無需自己創(chuàng)建與控制Thread對象。
比如,你不用在代碼中編寫new Thread或者thread1.start()也一樣可以使用多線程。如下例:
ExecutorService exec = Executors.newCachedThreadPool(); for (int i = 0; i < 5; i++) {//5個任務exec.submit(new Runnable() {@Overridepublic void run() { System.out.println(Thread.currentThread().getName()+" doing task");}}); } exec.shutdown(); //關閉線程池輸出如下:
pool-1-thread-2 doing task pool-1-thread-1 doing task pool-1-thread-3 doing task pool-1-thread-4 doing task pool-1-thread-5 doing task從輸出我們可以看到,exec使用了線程池1中的5個線程做了這幾個任務。
這個例子中exec這個Executor負責管理任務,所謂的任務在這里就是實現(xiàn)了Runnable接口的匿名內(nèi)部類。
至于要使用幾個線程,什么時候啟動這些線程,是用線程池還是用單個線程來完成這些任務,我們無需操心。完全由exec這個執(zhí)行器來負責。
在這里exec(newCachedThreadPool)指向是一個可以根據(jù)需求創(chuàng)建新線程的線程池。
2.1?Executors工具類
Executors相當于執(zhí)行器的工廠類,包含各種常用執(zhí)行器的工廠方法,可以直接創(chuàng)建常用的執(zhí)行器。幾種常用的執(zhí)行器(靜態(tài)方法)如下:
Executors.newCachedThreadPool();??????? //創(chuàng)建一個緩沖池,緩沖池容量大小為Integer.MAX_VALUE
Executors.newFixedThreadPool(int);??? //創(chuàng)建固定容量大小的緩沖池
Executors.newSingleThreadExecutor();?? //創(chuàng)建容量為1的緩沖池
Executors.newScheduledThreadPool(corePoolSize); ? ? ? // 創(chuàng)建一個定長線程池,支持定時及周期性任務執(zhí)行。
在java doc中,并不提倡我們直接使用ThreadPoolExecutor,而是經(jīng)常使用Executors類中提供的幾個靜態(tài)方法來創(chuàng)建線程池。
從下面它們的具體實現(xiàn)來看,它們實際上也是調(diào)用了ThreadPoolExecutor,只不過參數(shù)都已配置好了。
2.1.1 newCachedThreadPool()
public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }創(chuàng)建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程(線程池中曾經(jīng)創(chuàng)建的線程,在完成某個任務后也許會被用來完成另外一項任務),若無可回收,則新建線程。
實現(xiàn)原理:將corePoolSize設置為0,將maximumPoolSize設置為Integer.MAX_VALUE,
使用的是沒有容量的SynchronousQueue(無界),但其maximumPoolSize是無界的,也就是說來了任務就創(chuàng)建線程運行,并且當主線程提交任務的速度高于maximumPoolSize中線程處理任務的速度時CachedThreadPool將會不斷的創(chuàng)建新的線程。
適用于執(zhí)行很多的短期異步任務的小程序,或者是負載較輕的服務器。在極端情況下,CachedThreadPool會因為創(chuàng)建過多線程而耗盡CPU和內(nèi)存資源。
60L:當線程空閑超過60秒,就銷毀線程。
2.1.2 newFixedThreadPool(int nThreads)
public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }這個創(chuàng)建的線程池corePoolSize和maximum PoolSize值是相等的,都為nThreads。
keepAliveTime=0L:線程一空閑,就立即銷毀。
它使用的LinkedBlockingQueue(無界隊列)。使用該隊列作為工作隊列會對線程池產(chǎn)生如下影響:
(1)當前線程池中的線程數(shù)量達到corePoolSize后,新的任務將在無界隊列中等待;
(2)由于我們使用的是無界隊列,所以參數(shù)maximumPoolSize和keepAliveTime無效;
(3)由于使用無界隊列,運行中的FixedThreadPool不會拒絕任務(當然此時是未執(zhí)行shutdown和shutdownNow方法)。
所以不會去調(diào)用RejectExecutionHandler的rejectExecution方法拋出異常。
創(chuàng)建一個定長線程池(最大線程數(shù)是nThreads),可控制線程最大并發(fā)數(shù),超出的線程會在隊列LinkedBlockingQueue中等待。適用于為了滿足資源管理要求,而需要限制當前線程數(shù)量的應用場景。
線程在執(zhí)行完自己當前任務后,會在循環(huán)中反復從LinkedBlockingQueue獲取任務來執(zhí)行。
2.1.3 newSingleThreadExecutor()
public static ExecutorService newSingleThreadPool() {return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }創(chuàng)建一個單線程化的線程池,它只會用唯一的工作線程來執(zhí)行任務,保證所有任務按照指定順序(FIFO, LIFO, 優(yōu)先級)執(zhí)行。這種特點可以被用來處理共享資源的問題而不需要考慮同步的問題。
適用于需要保證順序地執(zhí)行各個任務,并且在任意時間點,不會有多個線程是活動的應用場景。
它使用的LinkedBlockingQueue(無界隊列)。
2.1.4 newSingleThreadScheduledExector()?
只包含一個線程的定時及周期線程池。即使任務再多,也只用1個線程完成任務。
2.1.5 newScheduledThreadPool ()
創(chuàng)建一個定長線程池,支持定時及周期性任務執(zhí)行。
適用于多個后臺線程執(zhí)行周期任務,同時為了滿足資源管理的需求而限制后臺線程的數(shù)量的應用場景。
2.1.6 表格對比
| SynchronousQueue 無界 | 0 | Integer.MAX_VALUE | 很多的短期異步任務的小程序,或者是負載較輕的服務器。 |
| LinkedBlockingQueue 無界 | =nThreads | =nThreads | 為了滿足資源管理要求,而需要限制當前線程數(shù)量 |
| LinkedBlockingQueue 無界 | 1 | 1 | 于需要保證順序地執(zhí)行各個任務,并且在任意時間點,不會有多個線程是活動 |
| ? | 一個線程 | ? | 定時及周期線程池 |
| ? | 定長線程池 | ? | 多個后臺線程執(zhí)行周期任務,限制后臺線程的數(shù)量 |
總結(jié)
- 上一篇: 线程安全、守护线程、join()
- 下一篇: 优化查询、访问量大时的优化