<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    關閉線程池 shutdown 和 shutdownNow 的區別

    VSole2021-12-18 16:26:04

    前言

    本章分為兩個議題

    如何正確關閉線程池

    shutdown 和 shutdownNow 的區別

    項目環境

    jdk 1.8

    github 地址:https://github.com/huajiexiewenfeng/java-concurrent

    本章模塊:threadpool

    1.線程池示例

    public class ShutDownThreadPoolDemo {
    
        private ExecutorService service = Executors.newFixedThreadPool(10);
        
        public static void main(String[] args) {
            new ShutDownThreadPoolDemo().executeTask();
        }
        
        public void executeTask() {
            for (int i = 0; i < 100; i++) {
                service.submit(() -> {
                    System.out.println(Thread.currentThread().getName() + "->執行");
                });
            }
        }
    
    }
    

    執行結果

    pool-1-thread-2->執行
    pool-1-thread-3->執行
    pool-1-thread-1->執行
    pool-1-thread-4->執行
    pool-1-thread-5->執行
    pool-1-thread-6->執行
    ...
    

    執行完成之后,主線程會一直阻塞,那么如何關閉線程池呢?本章介紹 5 種在 ThreadPoolExecutor 中涉及關閉線程池的方法,如下所示

    • void shutdown
    • boolean isShutdown
    • boolean isTerminated
    • boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException
    • ListshutdownNow

    2.shutdown

    第一種方法叫作 shutdown(),它可以安全地關閉一個線程池,調用 shutdown() 方法之后線程池并不是立刻就被關閉,因為這時線程池中可能還有很多任務正在被執行,或是任務隊列中有大量正在等待被執行的任務,調用 shutdown() 方法后線程池會在執行完正在執行的任務和隊列中等待的任務后才徹底關閉。

    調用 shutdown() 方法后如果還有新的任務被提交,線程池則會根據拒絕策略直接拒絕后續新提交的任務。

    這段源碼位置(jdk 1.8 版本)

    java.util.concurrent.ThreadPoolExecutor#execute

    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        // 線程池中的線程比核心線程數少 
        if (workerCountOf(c) < corePoolSize) {
            // 新建一個核心線程執行任務
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        // 核心線程已滿,但是任務隊列未滿,添加到隊列中
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            // 任務成功添加到隊列以后,再次檢查是否需要添加新的線程,因為已存在的線程可能被銷毀了
            if (! isRunning(recheck) && remove(command))
                // 如果線程池處于非運行狀態,并且把當前的任務從任務隊列中移除成功,則拒絕該任務
                reject(command);
            else if (workerCountOf(recheck) == 0)
                // 如果之前的線程已經被銷毀完,新建一個非核心線程
                addWorker(null, false);
        }
        // 核心線程池已滿,隊列已滿,嘗試創建一個非核心新的線程
        else if (!addWorker(command, false))
            // 如果創建新線程失敗,說明線程池關閉或者線程池滿了,拒絕任務
            reject(command);
    }
    

    1373 行 if (! isRunning(recheck) && remove(command)) 如果線程池被關閉,將當前的任務從任務隊列中移除成功,并拒絕該任務

    1378 行 else if (!addWorker(command, false)) 如果創建新線程失敗,說明線程池關閉或者線程池滿了,拒絕任務。

    3.isShutdown

    第二個方法叫作 isShutdown(),它可以返回 true 或者 false 來判斷線程池是否已經開始了關閉工作,也就是是否執行了 shutdown 或者 shutdownNow 方法。

    這里需要注意,如果調用 isShutdown() 方法的返回的結果為 true 并不代表線程池此時已經徹底關閉了,這僅僅代表線程池開始了關閉的流程,也就是說,此時可能線程池中依然有線程在執行任務,隊列里也可能有等待被執行的任務。

    4.isTerminated

    第三種方法叫作 isTerminated(),這個方法可以檢測線程池是否真正“終結”了,這不僅代表線程池已關閉,同時代表線程池中的所有任務都已經都執行完畢了。

    比如我們上面提到的情況,如果此時已經調用了 shutdown 方法,但是還有任務沒有執行完,那么此時調用 isShutdown 方法返回的是 true,而 isTerminated 方法則會返回 false。

    直到所有任務都執行完畢了,調用 isTerminated() 方法才會返回 true,這表示線程池已關閉并且線程池內部是空的,所有剩余的任務都執行完畢了。

    5.awaitTermination

    第四個方法叫作 awaitTermination(),它本身并不是用來關閉線程池的,而是主要用來判斷線程池狀態的。

    比如我們給 awaitTermination 方法傳入的參數是 10 秒,那么它就會陷入 10 秒鐘的等待,直到發生以下三種情況之一:

    等待期間(包括進入等待狀態之前)線程池已關閉并且所有已提交的任務(包括正在執行的和隊列中等待的)都執行完畢,相當于線程池已經“終結”了,方法便會返回 true 等待超時時間到后,第一種線程池“終結”的情況始終未發生,方法返回 false 等待期間線程被中斷,方法會拋出 InterruptedException 異常 調用 awaitTermination 方法后當前線程會嘗試等待一段指定的時間,如果在等待時間內,線程池已關閉并且內部的任務都執行完畢了,也就是說線程池真正“終結”了,那么方法就返回 true,否則超時返回 fasle。

    6.shutdownNow

    最后一個方法是 shutdownNow(),它和 shutdown() 的區別就是多了一個 Now,表示立刻關閉的意思,不推薦使用這一種方式關閉線程池。

    在執行 shutdownNow 方法之后,首先會給所有線程池中的線程發送 interrupt 中斷信號,嘗試中斷這些任務的執行,然后會將任務隊列中正在等待的所有任務轉移到一個 List 中并返回,我們可以根據返回的任務 List 來進行一些補救的操作,例如記錄在案并在后期重試。

    shutdownNow 源碼如下:

    public List shutdownNow() {
        List tasks;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            advanceRunState(STOP);
            interruptWorkers();
            tasks = drainQueue();
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
        return tasks;
    }
    

    interruptWorkers

    讓每一個已經啟動的線程都中斷,這樣線程就可以在執行任務期間檢測到中斷信號并進行相應的處理,提前結束任務

    7.shutdown 和 shutdownNow 的區別?

    • shutdown 會等待線程池中的任務執行完成之后關閉線程池,而 shutdownNow 會給所有線程發送中斷信號,中斷任務執行,然后關閉線程池
    • shutdown 沒有返回值,而 shutdownNow 會返回關閉前任務隊列中未執行的任務集合(List)
    線程池線程阻塞
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    多線程是Java的一個難點,但是它也很有趣,聽說玩得溜得起飛的人,人生都開啟多線程模式了…
    前言 本章分為兩個議題 如何正確關閉線程 shutdown 和 shutdownNow 的區別 項目環境 jdk 1.8 github 地址:https://github.com/huajiexiewenfeng/java-concurrent 本章模塊:threadpool 1.線程示例
    通過閱讀本篇文章你將了解到: CompletableFuture的使用 CompletableFure異步和同步的性能測試 已經有了Future為什么仍需要在JDK1.8中引入CompletableFuture CompletableFuture的應用場景 對CompletableFuture的使用優化
    同時例如 jstack、jmap 等工具也是不囿于一個方面的問題的,基本上出問題就是df、free、top 三連,然后依次jstack、jmap伺候,具體問題具體分析即可。CPU 異常往往還是比較好定位的。
    先看一個小例子,明白如何創建一個 Caffeine 緩存實例。因為如果提前能預估緩存的使用大小,那么可以設置緩存的初始容量,以免緩存不斷地進行擴容,致使效率不高。
    任務模塊專注于任務的執行等操作,開發和維護更加簡單和高效;接收“調度中心”的執行請求、終止請求和日志請求等。XXL-JOB的不同任務之間并行調度、并行執行。同時支持任務終止。當任務”路由策略”選擇”故障轉移”時,當調度中心每次發起調度請求時,會按照順序對執行器發出心跳檢測請求,第一個檢測為存活狀態的執行器將會被選定并發送調度請求。
    一、異步執行 實現方式二種: 使用異步注解@aysnc、啟動類:添加@EnableAsync注解 JDK 8本身有一個非常好用的Future類——CompletableFuture
    最常見的情況是使用 Tomcat 作為 Java Web 服務器,使用 Spring 提供的開箱即用的強大 的功能,并依賴其他開源庫來完成負責的業務功能實現
    如果是在消費端丟失數據,那么多次消費結果完全一模一樣的幾率很低。這時已經fetch的數據還沒有處理完成但已經被commit掉,因此沒有機會再次被處理,數據丟失。網絡負載很高或者磁盤很忙寫入失敗的情況下,沒有自動重試重發消息。
    crawlergo是一個使用chrome headless模式進行URL收集的瀏覽器爬蟲。它對整個網頁的關鍵位置與DOM渲染階段進行HOOK,自動進行表單填充并提交,配合智能的JS事件觸發,盡可能的收集網站暴露出的入口。內置URL去重模塊,過濾掉了大量偽靜態URL,對于大型網站仍保持較快的解析與抓取速度,最后得到高質量的請求結果集合。調研1.
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类