ExecutorService接口繼承了Executor接口,定義了一些生命周期的方法
public interface ExecutorService extends Executor { void shutdown(); List<Runnable> shutdownNow(); boolean isShutdown(); boolean isTerminated(); boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException; }
本文,我們逐一分析裡面的每個方法。
首先,我們需要創建一個任務代碼,這段任務代碼 主要是隨機生成含有10個字符的字符串
/** * 隨機生成10個字符的字符串 * @author dream-victor * */ public class Task1 implements Callable<String> { @Override public String call() throws Exception { String base = "abcdefghijklmnopqrstuvwxyz0123456789"; Random random = new Random(); StringBuffer sb = new StringBuffer(); for (int i = 0; i < 10; i++) { int number = random.nextInt(base.length()); sb.append(base.charAt(number)); } return sb.toString(); } }
然後,我們還需要一個長任務,這裡我們默認是沉睡10秒
/** * 長時間任務 * * @author dream-victor * */ public class LongTask implements Callable<String> { @Override public String call() throws Exception { TimeUnit.SECONDS.sleep(10); return "success"; } }
OK,所有前期准備完畢,下面我們就來分析一下ExecutorService接口中和生命周期有關的這些方 法:
1、shutdown方法:這個方法會平滑地關閉ExecutorService,當我們調用這個方法時, ExecutorService停止接受任何新的任務且等待已經提交的任務執行完成(已經提交的任務會分兩類:一類是已 經在執行的,另一類是還沒有開始執行的),當所有已經提交的任務執行完畢後將會關閉ExecutorService。這 裡我們先不舉例在下面舉例。
2、awaitTermination方法:這個方法有兩個參數,一個是timeout即超 時時間,另一個是unit即時間單位。這個方法會使線程等待timeout時長,當超過timeout時間後,會監測 ExecutorService是否已經關閉,若關閉則返回true,否則返回false。一般情況下會和shutdown方法組合使用 。例如:
ExecutorService service = Executors.newFixedThreadPool(4); service.submit(new Task1()); service.submit(new Task1()); service.submit(new LongTask()); service.submit(new Task1()); service.shutdown(); while (!service.awaitTermination(1, TimeUnit.SECONDS)) { System.out.println("線程池沒有關閉"); } System.out.println("線程池已經關閉");
這段代碼中,我們在第三次提交了一個長任務,這個任務 將執行10秒沉睡,緊跟著執行了一次shutdown()方法,假設:這時ExecutorService被立即關閉,下面調用 service.awaitTermination(1, TimeUnit.SECONDS)方法時應該返回true,程序執行結果應該只會打印出:“ 線程池已經關閉”。但是,真實的運行結果如下:
線程池沒有關閉 線程池沒有關閉 線程池沒有關閉 線程池沒有關閉 線程池沒有關閉 線程池沒有關閉 線程池沒有關閉 線程池沒有關閉 線程池沒有關閉 線程池已經關閉
這說明我們假設錯誤,service.awaitTermination(1, TimeUnit.SECONDS)每隔一秒 監測一次ExecutorService的關閉情況,而長任務正好需要執行10秒,因此會在前9秒監測時ExecutorService 為未關閉狀態,而在第10秒時已經關閉,因此第10秒時輸出:線程池已經關閉。這也驗證了shutdown方法關閉 ExecutorService的條件。
3、shutdownNow方法:這個方法會強制關閉ExecutorService,它將取消所 有運行中的任務和在工作隊列中等待的任務,這個方法返回一個List列表,列表中返回的是等待在工作隊列中 的任務。例如:
ExecutorService service = Executors.newFixedThreadPool(3); service.submit(new LongTask()); service.submit(new LongTask()); service.submit(new LongTask()); service.submit(new LongTask()); service.submit(new LongTask()); List<Runnable> runnables = service.shutdownNow(); System.out.println(runnables.size()); while (!service.awaitTermination(1, TimeUnit.MILLISECONDS)) { System.out.println("線程池沒有關閉"); } System.out.println("線程池已經關閉");
這段代碼中,我們限制了線程池的長度是3,提交了5個任 務,這樣將有兩個任務在工作隊列中等待,當我們執行shutdownNow方法時,ExecutorService被立刻關閉,所 以在service.awaitTermination(1, TimeUnit.MILLISECONDS)方法校驗時返回的是false,因此沒有輸出:線 程池沒有關閉。而在調用shutdownNow方法時,我們接受到了一個List,這裡包含的是在工作隊列中等待執行 的任務,由於線程池長度為3,且執行的都是長任務,所以當提交了三個任務後線程池已經滿了,剩下的兩次 提交只能在工作隊列中等待,因此我們看到runnables的大小為2,結果如下:
2 線程池已經關閉
4、isTerminated方法:這個方法會校驗ExecutorService當前的狀態是否為 “TERMINATED”即關閉狀態,當為“TERMINATED”時返回true否則返回false。例如:
ExecutorService service = Executors.newFixedThreadPool(3); service.submit(new Task1()); service.submit(new Task1()); service.submit(new LongTask()); service.shutdown(); System.out.println(System.currentTimeMillis()); while (!service.isTerminated()) { } System.out.println(System.currentTimeMillis ());
1303298818621 1303298828634 相差:10013毫秒,轉換一下除以1000,得到相差大約10秒
這10秒正 好是長任務執行的時間,因此在 ExecutorService正常關閉後isTerminated方法返回true。
5、 isShutdown方法:這個方法在ExecutorService關閉後返回true,否則返回false。方法比較簡單不再舉例。
以上討論是基於ThreadPoolExecutor的實現,不同的實現會有所不同需注意。