天天看點

Java并發程式設計一如何關閉線程池

推薦:​​Java并發程式設計彙總​​

Java并發程式設計一如何關閉線程池

當線程池中已經有大量線程正在處理任務,并且任務隊列中也有很多任務正在等待被處理,這個時候我們該如何去關閉線程池呢?線程池的後續處理又是怎麼的呢?

首先我們來建立一個任務。

任務

​ShutDownTask​

​​類實作了​

​Runnable​

​​接口,它的​

​run()​

​​就是我們線程所要執行的任務,我們這裡的任務非常簡單,就是輸出自己(目前線程)的名稱,再​

​sleep(500)​

​即可。

class ShutDownTask implements Runnable {

    @Override
    public void run() {
        try {
            Thread.sleep(500);
            System.out.println(Thread.currentThread().getName());
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName() + "被中斷了");
        }
    }
}      

shutdown()

我們先來測試一下​

​shutdown()​

​​的作用。

測試代碼:

package threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ShutDown {

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 100; i++) {
            executorService.execute(new ShutDownTask());
        }
        Thread.sleep(1000);
        executorService.shutdown();
    }
}      

輸出

pool-1-thread-6
pool-1-thread-5
pool-1-thread-7
pool-1-thread-9
pool-1-thread-10
pool-1-thread-4
pool-1-thread-2
pool-1-thread-8
pool-1-thread-1
pool-1-thread-3
pool-1-thread-5
pool-1-thread-6
pool-1-thread-8
pool-1-thread-10
pool-1-thread-2      

我隻粘貼了一部分輸出,其實會輸出​

​100​

​​行,也就是線程池會處理完所有的任務,既包括正在執行的任務,還包括已經送出到任務隊列上的任務,我們從代碼也可以分析出來,每個任務會​

​sleep(500)​

​​,而在​

​main​

​​線程中送出任務後,隻​

​sleep(1000)​

​​,而​

​main​

​​線程​

​sleep(1000)​

​​後,很顯然所有的任務是不可能已經被全部執行完的,而我們這裡定義的線程池是​

​FixedThreadPool​

​​,它最多隻能存在​

​10​

​​個線程去執行任務,是以肯定有任務在任務隊列中,而當​

​main​

​​線程調用線程池的​

​shutdown()​

​​後,所有任務依然全部執行完了,可以說明​

​shutdown()​

​會處理完所有的任務,既包括正在執行的任務,還包括已經送出到任務隊列上的任務。

可以得出,調用​

​shutdown()​

​後線程池的後續操作:

  1. 停止接收新​

    ​submit​

    ​的任務。
  2. 已經送出的任務(包括正在執行的任務和隊列中等待的任務),都會繼續執行完成。
  3. 等到第​

    ​2​

    ​步完成後,才真正停止。

shutdown()後線程池是否還能送出任務?

測試代碼:

package threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ShutDown {

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 100; i++) {
            executorService.execute(new ShutDownTask());
        }
        Thread.sleep(1000);

        executorService.shutdown();
        executorService.execute(new ShutDownTask());
    }
}      
Java并發程式設計一如何關閉線程池

很明顯是不能再送出任務的,線程池拒絕了送出的任務,之前說過,當線程池滿了(不能再建立線程去處理任務,即線程數為最大線程數​

​maximumPoolSize​

​,任務隊列也裝滿了任務)之後也會拒絕送出的任務,這裡又多了一種情況。

如果我們就想讓線程池現在、馬上、立刻就停止呢?接着看。

shutdownNow()

測試代碼:

package threadpool;

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ShutDown {

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 100; i++) {
            executorService.execute(new ShutDownTask());
        }
        Thread.sleep(1000);
        List<Runnable> runnableList = executorService.shutdownNow();
    }
}      

輸出:

pool-1-thread-8
pool-1-thread-4
pool-1-thread-2
pool-1-thread-3
pool-1-thread-1
pool-1-thread-6
pool-1-thread-7
pool-1-thread-9
pool-1-thread-10
pool-1-thread-5
pool-1-thread-3
pool-1-thread-1
pool-1-thread-2
pool-1-thread-4
pool-1-thread-8
pool-1-thread-5
pool-1-thread-6
pool-1-thread-10
pool-1-thread-7
pool-1-thread-9
pool-1-thread-2被中斷了
pool-1-thread-5被中斷了
pool-1-thread-3被中斷了
pool-1-thread-6被中斷了
pool-1-thread-4被中斷了
pool-1-thread-8被中斷了
pool-1-thread-1被中斷了      

這是全部輸出了,很顯然正在執行的任務會被中斷執行,在任務隊列中的線程會被直接忽略。

Attempts to stop all actively executing tasks, halts the processing of waiting tasks, and returns a list of the tasks that were awaiting execution.

翻譯:嘗試停止所有正在執行的任務,停止處理等待的任務,并傳回等待執行任務的清單。

上面代碼中的​​

​runnableList​

​ 就是等待執行任務的清單,以便程式可以做後續處理,如重新建立線程池去處理等。

調用此方法當然也會拒絕新送出的任務,大家可以自己去試一試,要多動手敲代碼,代碼才會認識你。

可以得出,調用​

​shutdownNow()​

​後線程池的後續操作:

  1. 跟​

    ​shutdown()​

    ​​一樣,先停止接收新​

    ​submit​

    ​的任務。
  2. 忽略任務隊列裡等待的任務。
  3. 嘗試将正在執行的任務​

    ​interrupt​

    ​中斷。
  4. 傳回未執行的任務清單。

線程池試圖終止線程的方法是通過調用​

​interrupt()​

​​方法來實作的,這種方法的作用有限,如果線程中沒有​

​sleep​

​​ 、​

​wait​

​​、​

​Condition​

​​、定時鎖等應用, ​

​interrupt()​

​​方法是無法中斷目前的線程的。是以,​

​shutdownNow()​

​并不代表線程池就一定立即能退出,它也可能必須要等待所有正在執行的任務都執行完成了才能退出。但是大多數時候是能立即退出的。

awaitTermination()

測試代碼:

package threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ShutDown {

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 100; i++) {
            executorService.execute(new ShutDownTask());
        }
        Thread.sleep(1500);

        executorService.shutdown();
        boolean b = executorService.awaitTermination(1L, TimeUnit.SECONDS);
        System.out.println(b);
    }
}      

結果:

Java并發程式設計一如何關閉線程池

測試代碼:

package threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ShutDown {

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 100; i++) {
            executorService.execute(new ShutDownTask());
        }
        Thread.sleep(1500);

        executorService.shutdown();
        boolean b = executorService.awaitTermination(7L, TimeUnit.SECONDS);
        System.out.println(b);
    }
}      

結果:

Java并發程式設計一如何關閉線程池
/**
     * 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
     */      
  • 所有已送出的任務(包括正在執行的任務和隊列中等待的任務)執行完。
  • 逾時時間到了(​

    ​timeout​

    ​​ 和 ​

    ​TimeUnit​

    ​設定的時間)。
  • 線程被中斷,抛出​

    ​InterruptedException​

    ​。