Executor(一)ExecutorService 線程池


Executor(一)ExecutorService 線程池

本篇主要涉及到的是 java.util.concurrent 包中的 ExecutorService。ExecutorService 就是 Java 中對線程池的實現。

一、ExecutorService 介紹

Java API 對 ExecutorService 接口的實現有兩個,它們是 Java 線程池具體實現類):

  1. ThreadPoolExecutor
  2. ScheduledThreadPoolExecutor

Executor 類圖

二、ExecutorService 創建

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

  1. newCachedThreadPool 創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程。
  2. newFixedThreadPool 創建一個定長線程池,可控制線程最大並發數,超出的線程會在隊列中等待。
  3. newScheduledThreadPool 創建一個定長線程池,支持定時及周期性任務執行。
  4. 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 關閉。

  1. shutdown() :在調用 shutdown() 方法之后,ExecutorService 不會立即關閉,但是它不再接收新的任務,直到當前所有線程執行完成才會關閉,所有在 shutdown() 執行之前提交的任務都會被執行。

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

  3. isShutdown():當調用 shutdown() 方法后返回為 true。

  4. isTerminated():當調用 shutdown() 方法后,並且所有提交的任務完成后返回為 true。

  5. 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());

每天用心記錄一點點。內容也許不重要,但習慣很重要!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM