如何停止一個線程池?


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() 方法是無法中斷當前的線程的。

 

 

 來一道刷了進BAT的面試題?


免責聲明!

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



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