Java 並發工具包中 java.util.concurrent.ExecutorService 接口定義了線程池任務提交、獲取線程池狀態、線程池停止的方法等。
JDK 1.8 中,線程池的停止一般使用 shutdown()、shutdownNow()、shutdown() + awaitTermination(long timeout, TimeUnit unit) 方法。
1、shutdown() 方法源碼中解釋
* Initiates an orderly shutdown in which previously submitted * tasks are executed, but no new tasks will be accepted. * Invocation has no additional effect if already shut down.
- 有序關閉,已提交任務繼續執行
- 不接受新任務
2、shutdownNow() 方法源碼中解釋
* Attempts to stop all actively executing tasks, halts the * processing of waiting tasks, and returns a list of the tasks * that were awaiting execution.
- 嘗試停止所有正在執行的任務
- 停止等待執行的任務,並返回等待執行的任務列表
3、awaitTermination(long timeout, TimeUnit unit) 方法源碼中解釋
* Blocks until all tasks have completed execution after a shutdown * request, or the timeout occurs, or the current thread is * interrupted, whichever happens first. * * @param timeout the maximum time to wait * @param unit the time unit of the timeout argument * @return {@code true} if this executor terminated and * {@code false} if the timeout elapsed before termination * @throws InterruptedException if interrupted while waiting
- 收到關閉請求后,所有任務執行完成、超時、線程被打斷,阻塞直到三種情況任意一種發生
- 參數可以設置超時時間與超時單位
- 線程池關閉返回 true;超過設置時間未關閉,返回 false
實踐:
1、使用 Executors.newFixedThreadPool(int nThreads) 創建固定大小線程池,測試 shutdown() 方法
package constxiong.concurrency.a013; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * 測試固定數量線程池 shutdown() 方法 * @author ConstXiong */ public class TestFixedThreadPoolShutdown { public static void main(String[] args) { //創建固定 3 個線程的線程池 ExecutorService threadPool = Executors.newFixedThreadPool(3); //向線程池提交 10 個任務 for (int i = 1; i <= 10; i++) { final int index = i; threadPool.submit(() -> { System.out.println("正在執行任務 " + index); //休眠 3 秒 try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } }); } //休眠 4 秒 try { Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); } //關閉線程池 threadPool.shutdown(); } }
打印結果如下,可以看出,主線程向線程池提交了 10 個任務,休眠 4 秒后關閉線程池,線程池把 10 個任務都執行完成后關閉了。
正在執行任務 1 正在執行任務 3 正在執行任務 2 正在執行任務 4 正在執行任務 6 正在執行任務 5 正在執行任務 8 正在執行任務 9 正在執行任務 7 正在執行任務 10
2、使用 Executors.newFixedThreadPool(int nThreads) 創建固定大小線程池,測試 shutdownNow() 方法
package constxiong.concurrency.a013; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * 測試固定數量線程池 shutdownNow() 方法 * @author ConstXiong */ public class TestFixedThreadPoolShutdownNow { public static void main(String[] args) { //創建固定 3 個線程的線程池 ExecutorService threadPool = Executors.newFixedThreadPool(3); //向線程池提交 10 個任務 for (int i = 1; i <= 10; i++) { final int index = i; threadPool.submit(() -> { System.out.println("正在執行任務 " + index); //休眠 3 秒 try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } }); } //休眠 4 秒 try { Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); } //關閉線程池 List<Runnable> tasks = threadPool.shutdownNow(); System.out.println("剩余 " + tasks.size() + " 個任務未執行"); } }
打印結果如下,可以看出,主線程向線程池提交了 10 個任務,休眠 4 秒后關閉線程池,線程池執行了 6 個任務,拋出異常,打印返回的剩余未執行的任務個數。
正在執行任務 1 正在執行任務 2 正在執行任務 3 正在執行任務 4 正在執行任務 6 正在執行任務 5 剩余 4 個任務未執行 java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at constxiong.concurrency.a013.TestFixedThreadPoolShutdownNow.lambda$0(TestFixedThreadPoolShutdownNow.java:24) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at constxiong.concurrency.a013.TestFixedThreadPoolShutdownNow.lambda$0(TestFixedThreadPoolShutdownNow.java:24) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748) java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at constxiong.concurrency.a013.TestFixedThreadPoolShutdownNow.lambda$0(TestFixedThreadPoolShutdownNow.java:24) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:748)
3、Executors.newFixedThreadPool(int nThreads) 創建固定大小線程池,測試 awaitTermination(long timeout, TimeUnit unit) 方法
package constxiong.concurrency.a013; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** * 測試固定數量線程池 shutdownNow() 方法 * @author ConstXiong */ public class TestFixedThreadPoolAwaitTermination { public static void main(String[] args) { //創建固定 3 個線程的線程池 ExecutorService threadPool = Executors.newFixedThreadPool(3); //向線程池提交 10 個任務 for (int i = 1; i <= 10; i++) { final int index = i; threadPool.submit(() -> { System.out.println("正在執行任務 " + index); //休眠 3 秒 try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } }); } //關閉線程池,設置等待超時時間 3 秒 System.out.println("設置線程池關閉,等待 3 秒..."); threadPool.shutdown(); try { boolean isTermination = threadPool.awaitTermination(3, TimeUnit.SECONDS); System.out.println(isTermination ? "線程池已停止" : "線程池未停止"); } catch (InterruptedException e) { e.printStackTrace(); } //再等待超時時間 20 秒 System.out.println("再等待 20 秒..."); try { boolean isTermination = threadPool.awaitTermination(20, TimeUnit.SECONDS); System.out.println(isTermination ? "線程池已停止" : "線程池未停止"); } catch (InterruptedException e) { e.printStackTrace(); } } }
打印結果如下,可以看出,主線程向線程池提交了 10 個任務,申請關閉線程池 3 秒超時,3 秒后線程池並未成功關閉;再獲取線程池關閉狀態 20 秒超時,線程池成功關閉。
正在執行任務 1 正在執行任務 3 正在執行任務 2 設置線程池關閉,等待 3 秒... 線程池未停止 正在執行任務 4 正在執行任務 6 再等待 20 秒... 正在執行任務 5 正在執行任務 7 正在執行任務 9 正在執行任務 8 正在執行任務 10 線程池已停止
總結:
1、調用 shutdown() 和 shutdownNow() 方法關閉線程池,線程池都無法接收新的任務
2、shutdown() 方法會繼續執行正在執行未完成的任務;shutdownNow() 方法會嘗試停止所有正在執行的任務
3、shutdown() 方法沒有返回值;shutdownNow() 方法返回等待執行的任務列表
4、awaitTermination(long timeout, TimeUnit unit) 方法可以獲取線程池是否已經關閉,需要配合 shutdown() 使用
5、shutdownNow() 不一定能夠立馬結束線程池,該方法會嘗試停止所有正在執行的任務,通過調用 Thread.interrupt() 方法來實現的,如果線程中沒有 sleep() 、wait()、Condition、定時鎖等應用, interrupt() 方法是無法中斷當前的線程的。