Executor(一)ExecutorService 線程池
本篇主要涉及到的是 java.util.concurrent 包中的 ExecutorService。ExecutorService 就是 Java 中對線程池的實現。
一、ExecutorService 介紹
Java API 對 ExecutorService 接口的實現有兩個,它們是 Java 線程池具體實現類):
- ThreadPoolExecutor
- ScheduledThreadPoolExecutor
二、ExecutorService 創建
創建一個什么樣的 ExecutorService 的實例(即線程池)需要根據具體應用場景而定,不過 Java 給我們提供了一個 Executors 工廠類,它可以幫助我們很方便的創建各種類型 ExecutorService 線程池,Executors 一共可以創建下面這四類線程池:
- newCachedThreadPool 創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程。
- newFixedThreadPool 創建一個定長線程池,可控制線程最大並發數,超出的線程會在隊列中等待。
- newScheduledThreadPool 創建一個定長線程池,支持定時及周期性任務執行。
- newSingleThreadExecutor 創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO)
備注:Executors 只是一個工廠類,它所有的方法返回的都是 ThreadPoolExecutor、ScheduledThreadPoolExecutor 這兩個類的實例。
三、ExecutorService 使用
public interface ExecutorService extends Executor {
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit) throws InterruptedException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
void shutdown();
List<Runnable> shutdownNow();
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
}
public interface Executor {
void execute(Runnable command);
}
3.1 execute(Runnable task)
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(() -> {
System.out.println("Asynchronous task");
});
executorService.shutdown();
這個方法有個問題,就是沒有辦法獲知 task 的執行結果。如果我們想獲得 task 的執行結果,我們可以傳入一個 Callable 的實例(下面會介紹)。
3.2 submit(Runnable task)
submit(Runnable) 和 execute(Runnable) 區別是前者可以返回一個 Future 對象,通過返回的 Future 對象,我們可以檢查提交的任務是否執行完畢,請看下面執行的例子:
Future future = executorService.submit(() -> {
System.out.println("Asynchronous task");
});
future.get(); //returns null if the task has finished correctly.
如果任務執行完成,future.get() 方法會返回一個 null。注意,future.get()方法會產生阻塞。
3.3 submit(Callable task)
submit(Callable) 和 submit(Runnable) 類似,也會返回一個 Future 對象,但是除此之外,submit(Callable) 接收的是一個 Callable 的實現,Callable 接口中的 call() 方法有一個返回值,可以返回任務的執行結果,而 Runnable 接口中的 run() 方法是 void 的,沒有返回值。請看下面實例:
Future<?> future = executorService.submit(() -> {
System.out.println("Asynchronous task");
return "binarylei";
});
System.out.println("future.get() = " + future.get());
如果任務執行完成,future.get() 方法會返回 Callable 任務的執行結果。注意,future.get() 方法會產生阻塞。
3.4 invokeAny(Collection tasks)
invokeAny(...) 方法接收的是一個 Callable 的集合,執行這個方法不會返回 Future,但是會返回所有 Callable 任務中其中一個任務的執行結果。這個方法也無法保證返回的是哪個任務的執行結果,反正是其中的某一個。請看下面實例:
Set<Callable<String>> tasks = new HashSet<>();
for (int i = 0; i < 5; i++) {
int num = i;
tasks.add(new Callable<String>() {
@Override
public String call() throws Exception {
return String.valueOf(num);
}
});
}
System.out.println(executorService.invokeAny(tasks));
大家可以嘗試執行上面代碼,每次執行都會返回一個結果,並且返回的結果是變化的,可能會返回“Task2”也可是“Task1”或者其它。
3.5 invokeAll(Collection tasks)
invokeAll(...) 與 invokeAny(...) 類似也是接收一個 Callable 集合,但是前者執行之后會返回一個 Future 的 List,其中對應着每個 Callable 任務執行后的 Future 對象。情況下面這個實例:
Set<Callable<String>> tasks = new HashSet<>();
for (int i = 0; i < 5; i++) {
int num = i;
tasks.add(new Callable<String>() {
@Override
public String call() throws Exception {
return String.format("Task-%s", num);
}
});
}
List<Future<String>> futures = executorService.invokeAll(tasks);
for (int i = 0; i < futures.size(); i++) {
System.out.println(futures.get(i).get());
}
四、ExecutorService 關閉
當我們使用完成 ExecutorService 之后應該關閉它,否則它里面的線程會一直處於運行狀態。
舉個例子,如果的應用程序是通過 main() 方法啟動的,在這個 main() 退出之后,如果應用程序中的 ExecutorService 沒有關閉,這個應用將一直運行。之所以會出現這種情況,是因為 ExecutorService 中運行的線程會阻止 JVM 關閉。
-
shutdown() :在調用 shutdown() 方法之后,ExecutorService 不會立即關閉,但是它不再接收新的任務,直到當前所有線程執行完成才會關閉,所有在 shutdown() 執行之前提交的任務都會被執行。
-
shutdownNow() :立即關閉 ExecutorService,這個動作將跳過所有正在執行的任務和被提交還沒有執行的任務。但是它並不對正在執行的任務做任何保證,有可能它們都會停止,也有可能執行完成。
-
isShutdown():當調用 shutdown() 方法后返回為 true。
-
isTerminated():當調用 shutdown() 方法后,並且所有提交的任務完成后返回為 true。
-
awaitTermination(2, TimeUnit.SECONDS):當調用 shutdown() 方法后,2s 內執行完所有的任務返回 true,超時返回 false。
//1. ExecutorService 不會立即關閉,直到所有線程執行完成才會關閉,所有在shutdown()執行之前提交的任務都會被執行。
executorService.shutdown();
//2. 關閉ExecutorService,並不對正在執行的任務做任何保證,有可能它們都會停止,也有可能執行完成。
//executorService.shutdownNow();
//3. 當調用shutdown()方法后返回為true
System.out.println(executorService.isShutdown());
//4. 當調用shutdown()方法后,並且所有提交的任務完成后返回為true
System.out.println(executorService.isTerminated());
//5. 2s 內執行守任務則返回true,否則超時返回false
System.out.println(executorService.awaitTermination(2, TimeUnit.SECONDS));
//6. 執行完所有任務后退出
/*while (!executorService.isTerminated()) {
System.out.println("正在執行任務");
}*/
System.out.println(executorService.isTerminated());
每天用心記錄一點點。內容也許不重要,但習慣很重要!