1. 程式人生 > >Java執行緒池 ExecutorService

Java執行緒池 ExecutorService

一、ExecutorService介紹

ExecutorService是Java中對執行緒池定義的一個介面,它java.util.concurrent包中,在這個介面中定義了和後臺任務執行相關的方法: 
這裡寫圖片描述

Java API對ExecutorService介面的實現有兩個,所以這兩個即是Java執行緒池具體實現類(詳細瞭解這兩個實現類,點選這裡):

1. ThreadPoolExecutor
2. ScheduledThreadPoolExecutor
  • 1
  • 2
  • 3

除此之外,ExecutorService還繼承了Executor介面(注意區分Executor介面和Executors工廠類),這個介面只有一個execute()

方法,最後我們看一下整個繼承樹: 
這裡寫圖片描述

二、ExecutorService的建立

建立一個什麼樣的ExecutorService的例項(即執行緒池)需要g根據具體應用場景而定,不過Java給我們提供了一個Executors工廠類,它可以幫助我們很方便的建立各種型別ExecutorService執行緒池,Executors一共可以建立下面這四類執行緒池:

1. newCachedThreadPool 建立一個可快取執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閒執行緒,若無可回收,則新建執行緒。
2. newFixedThreadPool 建立一個定長執行緒池,可控制執行緒最大併發數,超出的執行緒會在佇列中等待。
3. newScheduledThreadPool 建立一個定長執行緒池,支援定時及週期性任務執行。
4. newSingleThreadExecutor 建立一個單執行緒化的執行緒池,它只會用唯一的工作執行緒來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先順序)執行。
  • 1
  • 2
  • 3
  • 4
  • 5

備註:Executors只是一個工廠類,它所有的方法返回的都是ThreadPoolExecutorScheduledThreadPoolExecutor這兩個類的例項。

三、ExecutorService的使用

ExecutorService executorService = Executors.newFixedThreadPool(10);

executorService.execute(new Runnable() {
public void run() {
    System.out.println("Asynchronous task");
}
});

executorService.shutdown();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

四、ExecutorService的執行

ExecutorService有如下幾個執行方法:

- execute(Runnable)
- submit(Runnable)
- submit(Callable)
- invokeAny(...)
- invokeAll(...)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

4.1 execute(Runnable)

這個方法接收一個Runnable例項,並且非同步的執行,請看下面的例項:

ExecutorService executorService = Executors.newSingleThreadExecutor();

executorService.execute(new Runnable() {
public void run() {
    System.out.println("Asynchronous task");
}
});

executorService.shutdown();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

這個方法有個問題,就是沒有辦法獲知task的執行結果。如果我們想獲得task的執行結果,我們可以傳入一個Callable的例項(下面會介紹)。

4.2 submit(Runnable)

submit(Runnable)execute(Runnable)區別是前者可以返回一個Future物件,通過返回的Future物件,我們可以檢查提交的任務是否執行完畢,請看下面執行的例子:

Future future = executorService.submit(new Runnable() {
public void run() {
    System.out.println("Asynchronous task");
}
});

future.get();  //returns null if the task has finished correctly.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

如果任務執行完成,future.get()方法會返回一個null。注意,future.get()方法會產生阻塞。

4.3 submit(Callable)

submit(Callable)submit(Runnable)類似,也會返回一個Future物件,但是除此之外,submit(Callable)接收的是一個Callable的實現,Callable介面中的call()方法有一個返回值,可以返回任務的執行結果,而Runnable介面中的run()方法是void的,沒有返回值。請看下面例項:

Future future = executorService.submit(new Callable(){
public Object call() throws Exception {
    System.out.println("Asynchronous Callable");
    return "Callable Result";
}
});

System.out.println("future.get() = " + future.get());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

如果任務執行完成,future.get()方法會返回Callable任務的執行結果。注意,future.get()方法會產生阻塞。

4.4 invokeAny(…)

invokeAny(...)方法接收的是一個Callable的集合,執行這個方法不會返回Future,但是會返回所有Callable任務中其中一個任務的執行結果。這個方法也無法保證返回的是哪個任務的執行結果,反正是其中的某一個。請看下面例項:

ExecutorService executorService = Executors.newSingleThreadExecutor();

Set<Callable<String>> callables = new HashSet<Callable<String>>();

callables.add(new Callable<String>() {
public String call() throws Exception {
    return "Task 1";
}
});
callables.add(new Callable<String>() {
public String call() throws Exception {
    return "Task 2";
}
});
callables.add(new Callable<String>() {
    public String call() throws Exception {
    return "Task 3";
}
});

String result = executorService.invokeAny(callables);
System.out.println("result = " + result);
executorService.shutdown();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

大家可以嘗試執行上面程式碼,每次執行都會返回一個結果,並且返回的結果是變化的,可能會返回“Task2”也可是“Task1”或者其它。

4.5 invokeAll(…)

invokeAll(...)與 invokeAny(...)類似也是接收一個Callable集合,但是前者執行之後會返回一個Future的List,其中對應著每個Callable任務執行後的Future物件。情況下面這個例項:

ExecutorService executorService = Executors.newSingleThreadExecutor();

Set<Callable<String>> callables = new HashSet<Callable<String>>();

callables.add(new Callable<String>() {
public String call() throws Exception {
    return "Task 1";
}
});
callables.add(new Callable<String>() {
    public String call() throws Exception {
    return "Task 2";
}
});
callables.add(new Callable<String>() {
public String call() throws Exception {
    return "Task 3";
}
});

List<Future<String>> futures = executorService.invokeAll(callables);

for(Future<String> future : futures){
System.out.println("future.get = " + future.get());
}

executorService.shutdown();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

五、ExecutorService的關閉

當我們使用完成ExecutorService之後應該關閉它,否則它裡面的執行緒會一直處於執行狀態。

舉個例子,如果的應用程式是通過main()方法啟動的,在這個main()退出之後,如果應用程式中的ExecutorService沒有關閉,這個應用將一直執行。之所以會出現這種情況,是因為ExecutorService中執行的執行緒會阻止JVM關閉。

如果要關閉ExecutorService中執行的執行緒,我們可以呼叫ExecutorService.shutdown()方法。在呼叫shutdown()方法之後,ExecutorService不會立即關閉,但是它不再接收新的任務,直到當前所有執行緒執行完成才會關閉,所有在shutdown()執行之前提交的任務都會被執行。

如果我們想立即關閉ExecutorService,我們可以呼叫ExecutorService.shutdownNow()方法。這個動作將跳過所有正在執行的任務和被提交還沒有執行的任務。但是它並不對正在執行的任務做任何保證,有可能它們都會停止,也有可能執行完成。

(1). newCachedThreadPool
建立一個可快取執行緒池,如果執行緒池長度超過處理需要,可靈活回收空閒執行緒,若無可回收,則新建執行緒。示例程式碼如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) { final int index = i; try { Thread.sleep(index * 1000); catch (InterruptedException e) { e.printStackTrace();