在Java 5.0之前啟動一個任務是通過調用Thread類的start()方法來實現的,任務的提於交和執行是同時進行的,如果你想對任務的執行進行調度或是控制 同時執行的線程數量就需要額外編寫代碼來完成。5.0裡提供了一個新的任務執行架構使你可以輕鬆地調度和控制任務的執行,並且可以建立一個類似資料庫連接 池的線程池來執行任務。這個架構主要有三個介面和其相應的具體類組成。這三個介面是Executor, ExecutorService、ScheduledExecutorService,讓我們先用一個圖來顯示它們的關係:
圖的左側是介面,圖的右側是這些介面的具體類。注意Executor是沒有直接具體實現的。
Executor介面:是用來執行Runnable任務的,它只定義一個方法:
- execute(Runnable command):執行Ruannable類型的任務
ExecutorService介面:ExecutorService繼承了Executor的方法,並提供了執行Callable任務和中止任務執行的服務,其定義的方法主要有:
- submit(task):可用來提交Callable或Runnable任務,並返回代表此任務的Future對象
- invokeAll(collection of tasks):批處理任務集合,並返回一個代表這些任務的Future對象集合
- shutdown():在完成已提交的任務後關閉服務,不再接受新任務
- shutdownNow():停止所有正在執行的任務並關閉服務。
- isTerminated():測試是否所有任務都執行完畢了。
- isShutdown():測試是否該ExecutorService已被關閉
ScheduledExecutorService介面在ExecutorService的基礎上,ScheduledExecutorService提供了按時間安排執行任務的功能,它提供的方法主要有:
- schedule(task, initDelay): 安排所提交的Callable或Runnable任務在initDelay指定的時間後執行。
- scheduleAtFixedRate():安排所提交的Runnable任務按指定的間隔重複執行
- scheduleWithFixedDelay():安排所提交的Runnable任務在每次執行完後,等待delay所指定的時間後重複執行。
重要的Executors類
雖然以上提到的介面有其實現的具體類,但為了方便Java 5.0建議使用Executors的工具類來得到Executor介面的具體對象,需要注意的是Executors是一個類,不是Executor的複數 形式。Executors提供了以下一些static的方法:
- callable(Runnable task): 將Runnable的任務轉化成Callable的任務
- newSingleThreadExecutor: 產生一個ExecutorService對象,這個對象只有一個線程可用來執行任務,若任務多於一個,任務將按先後順序執行。
- newCachedThreadPool(): 產生一個ExecutorService對象,這個對象帶有一個線程池,線程池的大小會根據需要調整,線程執行完任務後返回線程池,供執行下一次任務使用。
- newFixedThreadPool(int poolSize):產生一個ExecutorService對象,這個對象帶有一個大小為poolSize的線程池,若任務數量大於poolSize,任務會被放在一個queue裡順序執行。
- newSingleThreadScheduledExecutor:產生一個ScheduledExecutorService對象,這個對象的線程池大小為1,若任務多於一個,任務將按先後順序執行。
- newScheduledThreadPool(int poolSize): 產生一個ScheduledExecutorService對象,這個對象的線程池大小為poolSize,若任務數量大於poolSize,任務會在一個queue裡等待執行
舉例說明:
應用Executors來建立Thread pool
有時候您需要建立一堆Thread來執行一些小任務,然而頻繁的建立Thread有時會是個開銷,因為Thread的建立必須與作業系統互動,如果能建立一個Thread pool來管理這些小的Thread並加以重複使用,對於系統效能會是個改善的方式。
您可以使用Executors來建立Thread pool,Executors有幾個static方法,列出如下:
| 方法 |
說明 |
| newCachedThreadPool |
建立可以快取的Thread,每個Thread預設可idle 60秒 |
newFixedThreadPool |
包括固定數量的Thread |
newSingleThreadExecutor |
只有一個Thread,循序的執行指定給它的每個任務 |
| newScheduledThreadPool |
可排程的Thread |
| newSingleThreadScheduledExecutor |
單一可排程的Thread |
舉個簡單的執行個體,下面的程式使用newFixedThreadPool方法建立Thread pool,當中包括五個可以重複使用的Thread,您可以指定Runnable物件給它,程式中會產生十個Runnable物件,由於Thread pool中只有五個可用的Thread,所以後來建立的五個Runnable必須等待有閒置Thread才會被執行:
package onlyfun.caterpillar;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class ExecutorDemo { public static void main(String[] args) { ExecutorService service = Executors.newFixedThreadPool(5); for(int i = 0; i < 10; i++) { final int count = i; service.submit(new Runnable() { public void run() { System.out.println(count); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } }); } service.shutdown(); // 最後記得關閉Thread pool }}
submit()方法也接受實作Callable介面的物件,最後傳回Future物件,可以取得Callable執行過後的傳回結果。
如果想利用Executors進行排程,例如排定某個工作30秒後執行:
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor( );
scheduler.schedule(new Runnable( ) {
public void run() {
// 排程工作
}
},
30, TimeUnit.SECONDS);
或排定某個工作5秒後執行,之後每30秒執行一次:
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor( );
final ScheduledFuture future = scheduler.scheduleAtFixedRate(new Runnable( ) {
public void run() {
// 排程工作
System.out.println("t");
}
},
0, 5, TimeUnit.SECONDS);
// 排定 60 秒後取消future
scheduler.schedule(new Runnable( ) {
public void run( ) {
future.cancel(false);
}
}, 60, TimeUnit.SECONDS);
如上所示,想要取消排程任務,可以呼叫ScheduledFuture的cancel()方法。