最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中如何判斷線程池任務(wù)已執(zhí)行完成

 更新時(shí)間:2022年05月10日 10:01:55   作者:??Java中文社群????  
這篇文章主要介紹了Java中如何判斷線程池任務(wù)已執(zhí)行完成,文章圍繞主題展開詳細(xì)相關(guān)資料,具有一定的參考價(jià)值需要的小伙伴可以參考以參考一下

前言:

很多場景下,我們需要等待線程池的所有任務(wù)都執(zhí)行完,然后再進(jìn)行下一步操作。對于線程 Thread 來說,很好實(shí)現(xiàn),加一個 join 方法就解決了,然而對于線程池的判斷就比較麻煩了。

我們本文提供 4 種判斷線程池任務(wù)是否執(zhí)行完的方法:

  • 使用 isTerminated 方法判斷。
  • 使用 getCompletedTaskCount 方法判斷。
  • 使用 CountDownLatch 判斷。
  • 使用 CyclicBarrier 判斷。

接下來我們一個一個來看。

不判斷的問題

如果不對線程池是否已經(jīng)執(zhí)行完做判斷,就會出現(xiàn)以下問題,如下代碼所示:

import java.util.Random;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolCompleted {
    public static void main(String[] args) {
        // 創(chuàng)建線程池
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 20,
                0, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1024));
        // 添加任務(wù)
        addTask(threadPool);
				// 打印結(jié)果
        System.out.println("線程池任務(wù)執(zhí)行完成!");
    }
    /**
     * 給線程池添加任務(wù)
     */
    private static void addTask(ThreadPoolExecutor threadPool) {
        // 任務(wù)總數(shù)
        final int taskCount = 5;
        // 添加任務(wù)
        for (int i = 0; i < taskCount; i++) {
            final int finalI = i;
            threadPool.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        // 隨機(jī)休眠 0-4s
                        int sleepTime = new Random().nextInt(5);
                        TimeUnit.SECONDS.sleep(sleepTime);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(String.format("任務(wù)%d執(zhí)行完成", finalI));
                }
            });
        }
    }
}

以上程序的執(zhí)行結(jié)果如下: 

 從上述執(zhí)行結(jié)果可以看出,程序先打印了“線程池任務(wù)執(zhí)行完成!”,然后還在陸續(xù)的執(zhí)行線程池的任務(wù),這種執(zhí)行順序混亂的結(jié)果,并不是我們期望的結(jié)果。我們想要的結(jié)果是等所有任務(wù)都執(zhí)行完之后,再打印“線程池任務(wù)執(zhí)行完成!”的信息。

產(chǎn)生以上問題的原因是因?yàn)橹骶€程 main,和線程池是并發(fā)執(zhí)行的,所以當(dāng)線程池還沒執(zhí)行完,main 線程的打印結(jié)果代碼就已經(jīng)執(zhí)行了。想要解決這個問題,就需要在打印結(jié)果之前,先判斷線程池的任務(wù)是否已經(jīng)全部執(zhí)行完,如果沒有執(zhí)行完就等待任務(wù)執(zhí)行完再執(zhí)行打印結(jié)果。

方法1:isTerminated

我們可以利用線程池的終止?fàn)顟B(tài)(TERMINATED)來判斷線程池的任務(wù)是否已經(jīng)全部執(zhí)行完,但想要線程池的狀態(tài)發(fā)生改變,我們就需要調(diào)用線程池的 shutdown 方法,不然線程池一直會處于 RUNNING 運(yùn)行狀態(tài),那就沒辦法使用終止?fàn)顟B(tài)來判斷任務(wù)是否已經(jīng)全部執(zhí)行完了,它的實(shí)現(xiàn)代碼如下:

import java.util.Random;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
 * 線程池任務(wù)執(zhí)行完成判斷
 */
public class ThreadPoolCompleted {
    public static void main(String[] args) {
        // 1.創(chuàng)建線程池
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 20,
                0, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1024));
        // 2.添加任務(wù)
        addTask(threadPool);
        // 3.判斷線程池是否執(zhí)行完
        isCompleted(threadPool); // 【核心調(diào)用方法】
        // 4.線程池執(zhí)行完
        System.out.println();
        System.out.println("線程池任務(wù)執(zhí)行完成!");
    }

    /**
     * 方法1:isTerminated 實(shí)現(xiàn)方式
     * 判斷線程池的所有任務(wù)是否執(zhí)行完
     */
    private static void isCompleted(ThreadPoolExecutor threadPool) {
        threadPool.shutdown();
        while (!threadPool.isTerminated()) { // 如果沒有執(zhí)行完就一直循環(huán)
        }
    }

    /**
     * 給線程池添加任務(wù)
     */
    private static void addTask(ThreadPoolExecutor threadPool) {
        // 任務(wù)總數(shù)
        final int taskCount = 5;
        // 添加任務(wù)
        for (int i = 0; i < taskCount; i++) {
            final int finalI = i;
            threadPool.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        // 隨機(jī)休眠 0-4s
                        int sleepTime = new Random().nextInt(5);
                        TimeUnit.SECONDS.sleep(sleepTime);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(String.format("任務(wù)%d執(zhí)行完成", finalI));
                }
            });
        }
    }
}

方法說明:shutdown 方法是啟動線程池有序關(guān)閉的方法,它在完全關(guān)閉之前會執(zhí)行完之前所有已經(jīng)提交的任務(wù),并且不會再接受任何新任務(wù)。當(dāng)線程池中的所有任務(wù)都執(zhí)行完之后,線程池就進(jìn)入了終止?fàn)顟B(tài),調(diào)用 isTerminated 方法返回的結(jié)果就是 true 了。

以上程序的執(zhí)行結(jié)果如下: 

缺點(diǎn)分析

需要關(guān)閉線程池。

擴(kuò)展:線程池的所有狀態(tài)

線程池總共包含以下 5 種狀態(tài):

  • RUNNING:運(yùn)行狀態(tài)。
  • SHUTDOWN:關(guān)閉狀態(tài)。
  • STOP:阻斷狀態(tài)。
  • TIDYING:整理狀態(tài)。
  • TERMINATED:終止?fàn)顟B(tài)。

 如果不調(diào)用線程池的關(guān)閉方法,那么線程池會一直處于 RUNNING 運(yùn)行狀態(tài)。

方法2:getCompletedTaskCount

我們可以通過判斷線程池中的計(jì)劃執(zhí)行任務(wù)數(shù)和已完成任務(wù)數(shù),來判斷線程池是否已經(jīng)全部執(zhí)行完,如果計(jì)劃執(zhí)行任務(wù)數(shù)=已完成任務(wù)數(shù),那么線程池的任務(wù)就全部執(zhí)行完了,否則就未執(zhí)行完,具體實(shí)現(xiàn)代碼如下:

/**
 * 方法2:getCompletedTaskCount 實(shí)現(xiàn)方式
 * 判斷線程池的所有任務(wù)是否執(zhí)行完
 */
private static void isCompletedByTaskCount(ThreadPoolExecutor threadPool) {
    while (threadPool.getTaskCount() != threadPool.getCompletedTaskCount()) {
    }
}

以上程序執(zhí)行結(jié)果如下:

方法說明

  • getTaskCount():返回計(jì)劃執(zhí)行的任務(wù)總數(shù)。由于任務(wù)和線程的狀態(tài)可能在計(jì)算過程中動態(tài)變化,因此返回的值只是一個近似值。
  • getCompletedTaskCount():返回完成執(zhí)行任務(wù)的總數(shù)。因?yàn)槿蝿?wù)和線程的狀態(tài)可能在計(jì)算過程中動態(tài)地改變,所以返回的值只是一個近似值,但是在連續(xù)的調(diào)用中并不會減少。

優(yōu)缺點(diǎn)分析

此實(shí)現(xiàn)方法的優(yōu)點(diǎn)是無需關(guān)閉線程池。 它的缺點(diǎn)是 getTaskCount() 和 getCompletedTaskCount() 返回的是一個近似值,因?yàn)榫€程池中的任務(wù)和線程的狀態(tài)可能在計(jì)算過程中動態(tài)變化,所以它們兩個返回的都是一個近似值。

方法3:CountDownLatch

CountDownLatch 可以理解為一個計(jì)數(shù)器,我們創(chuàng)建了一個包含 N 個任務(wù)的計(jì)數(shù)器,每個任務(wù)執(zhí)行完計(jì)數(shù)器 -1,直到計(jì)數(shù)器減為 0 時(shí),說明所有的任務(wù)都執(zhí)行完了,就可以執(zhí)行下一段業(yè)務(wù)的代碼了,它的實(shí)現(xiàn)流程如

下圖所示:

具體實(shí)現(xiàn)代碼如下:

public static void main(String[] args) throws InterruptedException {
    // 創(chuàng)建線程池
    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 20,
    	0, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1024));
    final int taskCount = 5;    // 任務(wù)總數(shù)
    // 單次計(jì)數(shù)器
    CountDownLatch countDownLatch = new CountDownLatch(taskCount); // ①
    // 添加任務(wù)
    for (int i = 0; i < taskCount; i++) {
        final int finalI = i;
        threadPool.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    // 隨機(jī)休眠 0-4s
                    int sleepTime = new Random().nextInt(5);
                    TimeUnit.SECONDS.sleep(sleepTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(String.format("任務(wù)%d執(zhí)行完成", finalI));
                // 線程執(zhí)行完,計(jì)數(shù)器 -1
                countDownLatch.countDown();  // ②
            }
        });
    }
    // 阻塞等待線程池任務(wù)執(zhí)行完
    countDownLatch.await();  // ③
    // 線程池執(zhí)行完
    System.out.println();
    System.out.println("線程池任務(wù)執(zhí)行完成!");
}

代碼說明:以上代碼中標(biāo)識為 ①、②、③ 的代碼行是核心實(shí)現(xiàn)代碼,其中: ① 是聲明一個包含了 5 個任務(wù)的計(jì)數(shù)器; ② 是每個任務(wù)執(zhí)行完之后計(jì)數(shù)器 -1; ③ 是阻塞等待計(jì)數(shù)器 CountDownLatch 減為 0,表示任務(wù)都執(zhí)行完了,可以執(zhí)行 await 方法后面的業(yè)務(wù)代碼了。

以上程序的執(zhí)行結(jié)果如下: 

優(yōu)缺點(diǎn)分析

CountDownLatch 寫法很優(yōu)雅,且無需關(guān)閉線程池,但它的缺點(diǎn)是只能使用一次,CountDownLatch 創(chuàng)建之后不能被重復(fù)使用,也就是說 CountDownLatch 可以理解為只能使用一次的計(jì)數(shù)器。

方法4:CyclicBarrier

CyclicBarrier 和 CountDownLatch 類似,它可以理解為一個可以重復(fù)使用的循環(huán)計(jì)數(shù)器,CyclicBarrier 可以調(diào)用 reset 方法將自己重置到初始狀態(tài),

CyclicBarrier 具體實(shí)現(xiàn)代碼如下:

public static void main(String[] args) throws InterruptedException {
    // 創(chuàng)建線程池
    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 20,
    	0, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1024));
    final int taskCount = 5;    // 任務(wù)總數(shù)
    // 循環(huán)計(jì)數(shù)器 ①
    CyclicBarrier cyclicBarrier = new CyclicBarrier(taskCount, new Runnable() {
        @Override
        public void run() {
            // 線程池執(zhí)行完
            System.out.println();
            System.out.println("線程池所有任務(wù)已執(zhí)行完!");
        }
    });
    // 添加任務(wù)
    for (int i = 0; i < taskCount; i++) {
        final int finalI = i;
        threadPool.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    // 隨機(jī)休眠 0-4s
                    int sleepTime = new Random().nextInt(5);
                    TimeUnit.SECONDS.sleep(sleepTime);
                    System.out.println(String.format("任務(wù)%d執(zhí)行完成", finalI));
                    // 線程執(zhí)行完
                    cyclicBarrier.await(); // ②
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

以上程序的執(zhí)行結(jié)果如下: 

方法說明

CyclicBarrier 有 3 個重要的方法:

  • 構(gòu)造方法:構(gòu)造方法可以傳遞兩個參數(shù),參數(shù) 1 是計(jì)數(shù)器的數(shù)量 parties,參數(shù) 2 是計(jì)數(shù)器為 0 時(shí),也就是任務(wù)都執(zhí)行完之后可以執(zhí)行的事件(方法)。
  • await 方法:在 CyclicBarrier 上進(jìn)行阻塞等待,當(dāng)調(diào)用此方法時(shí) CyclicBarrier 的內(nèi)部計(jì)數(shù)器會 -1,直到發(fā)生以下情形之一:
    • 在 CyclicBarrier 上等待的線程數(shù)量達(dá)到 parties,也就是計(jì)數(shù)器的聲明數(shù)量時(shí),則所有線程被釋放,繼續(xù)執(zhí)行。
    • 當(dāng)前線程被中斷,則拋出 InterruptedException 異常,并停止等待,繼續(xù)執(zhí)行。
    • 其他等待的線程被中斷,則當(dāng)前線程拋出 BrokenBarrierException 異常,并停止等待,繼續(xù)執(zhí)行。
    • 其他等待的線程超時(shí),則當(dāng)前線程拋出 BrokenBarrierException 異常,并停止等待,繼續(xù)執(zhí)行。
    • 其他線程調(diào)用 CyclicBarrier.reset() 方法,則當(dāng)前線程拋出 BrokenBarrierException 異常,并停止等待,繼續(xù)執(zhí)行。
  • reset 方法:使得CyclicBarrier回歸初始狀態(tài),直觀來看它做了兩件事:
    • 如果有正在等待的線程,則會拋出 BrokenBarrierException 異常,且這些線程停止等待,繼續(xù)執(zhí)行。將是否破損標(biāo)志位 broken 置為 false。

優(yōu)缺點(diǎn)分析

CyclicBarrier 從設(shè)計(jì)的復(fù)雜度到使用的復(fù)雜度都高于 CountDownLatch,相比于 CountDownLatch 來說它的優(yōu)點(diǎn)是可以重復(fù)使用(只需調(diào)用 reset 就能恢復(fù)到初始狀態(tài)),缺點(diǎn)是使用難度較高。

總結(jié)

我們本文提供 4 種判斷線程池任務(wù)是否執(zhí)行完的方法:

  • 使用 isTerminated 方法判斷:通過判斷線程池的完成狀態(tài)來實(shí)現(xiàn),需要關(guān)閉線程池,一般情況下不建議使用。
  • 使用 getCompletedTaskCount 方法判斷:通過計(jì)劃執(zhí)行總?cè)蝿?wù)量和已經(jīng)完成總?cè)蝿?wù)量,來判斷線程池的任務(wù)是否已經(jīng)全部執(zhí)行,如果相等則判定為全部執(zhí)行完成。但因?yàn)榫€程個體和狀態(tài)都會發(fā)生改變,所以得到的是一個大致的值,可能不準(zhǔn)確。
  • 使用 CountDownLatch 判斷:相當(dāng)于一個線程安全的單次計(jì)數(shù)器,使用比較簡單,且不需要關(guān)閉線程池,是比較常用的判斷方法。
  • 使用 CyclicBarrier 判斷:相當(dāng)于一個線程安全的重復(fù)計(jì)數(shù)器,但使用較為復(fù)雜,所以日常項(xiàng)目中使用的較少。

到此這篇關(guān)于Java中如何判斷線程池任務(wù)已執(zhí)行完成的文章就介紹到這了,更多相關(guān)Java線程池任務(wù)執(zhí)行內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java中避免NullPointerException的方法總結(jié)

    Java中避免NullPointerException的方法總結(jié)

    這篇文章主要介紹了Java中避免NullPointerException的方法總結(jié)的相關(guān)資料,需要的朋友可以參考下
    2017-07-07
  • Spring中的事件發(fā)布機(jī)制原理解析

    Spring中的事件發(fā)布機(jī)制原理解析

    這篇文章主要介紹了Spring中的事件發(fā)布機(jī)制原理解析,當(dāng)我們關(guān)心spring容器什么時(shí)候刷新,或者想在spring容器刷新的時(shí)候做一些事情,監(jiān)聽關(guān)心的事件,主要就是在ApplicationListener中寫對應(yīng)的事件,需要的朋友可以參考下
    2023-11-11
  • JAVA 繼承基本類、抽象類、接口介紹

    JAVA 繼承基本類、抽象類、接口介紹

    Java是一個面向?qū)ο蟮恼Z言,java面向?qū)ο笠话阌腥筇卣鳎悍庋b、繼承、多態(tài)
    2013-01-01
  • Java中的length和length()深入分析

    Java中的length和length()深入分析

    java中的length屬性是針對數(shù)組說的,比如說你聲明了一個數(shù)組,想知道這個數(shù)組的長度則用到了length這個屬性。java中的length()方法是針對字符串String說的,如果想看這個字符串的長度則用到length()這個方法。這篇文章將介紹幾個關(guān)于Java數(shù)組的關(guān)鍵概念。
    2016-11-11
  • Spring中的@CrossOrigin注冊處理方法源碼解析

    Spring中的@CrossOrigin注冊處理方法源碼解析

    這篇文章主要介紹了Spring中的@CrossOrigin注冊處理方法源碼解析,@CrossOrigin是基于@RequestMapping,@RequestMapping注釋方法掃描注冊的起點(diǎn)是equestMappingHandlerMapping.afterPropertiesSet(),需要的朋友可以參考下
    2023-12-12
  • Java實(shí)現(xiàn)刪除排序鏈表中的重復(fù)元素的方法

    Java實(shí)現(xiàn)刪除排序鏈表中的重復(fù)元素的方法

    這篇文章主要介紹了Java實(shí)現(xiàn)刪除排序鏈表中的重復(fù)元素的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • Mybatis-Plus多表關(guān)聯(lián)查詢的使用案例解析

    Mybatis-Plus多表關(guān)聯(lián)查詢的使用案例解析

    這篇文章主要介紹了Mybatis-Plus多表關(guān)聯(lián)查詢的使用,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-05-05
  • springboot接收excel數(shù)據(jù)文件去重方式

    springboot接收excel數(shù)據(jù)文件去重方式

    文章主要介紹了如何在Spring?Boot中實(shí)現(xiàn)文件上傳并入庫的功能,包括讀取Excel文件、生成Entity對象、使用MergeInto語句進(jìn)行數(shù)據(jù)庫操作以及注意事項(xiàng)
    2024-12-12
  • spring?boot常見get?、post請求參數(shù)處理、參數(shù)注解校驗(yàn)、參數(shù)自定義注解校驗(yàn)問題解析

    spring?boot常見get?、post請求參數(shù)處理、參數(shù)注解校驗(yàn)、參數(shù)自定義注解校驗(yàn)問題解析

    這篇文章主要介紹了spring?boot常見get?、post請求參數(shù)處理、參數(shù)注解校驗(yàn)、參數(shù)自定義注解校驗(yàn),本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-09-09
  • mybatis-plus之自動映射字段(typeHandler)的注意點(diǎn)及說明

    mybatis-plus之自動映射字段(typeHandler)的注意點(diǎn)及說明

    這篇文章主要介紹了mybatis-plus之自動映射字段(typeHandler)的注意點(diǎn)及說明,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-04-04

最新評論

阳高县| 疏勒县| 凯里市| 贵南县| 炎陵县| 云龙县| 明星| 永胜县| 都安| 密山市| 婺源县| 衡山县| 金乡县| 奇台县| 驻马店市| 肥乡县| 鸡西市| 花莲市| 仪陇县| 清水河县| 繁昌县| 洞头县| 兴化市| 铁岭县| 奈曼旗| 玉田县| 通榆县| 黔南| 刚察县| 兰溪市| 乾安县| 理塘县| 怀宁县| 前郭尔| 宣恩县| 垦利县| 汾西县| 仁寿县| 平安县| 江西省| 乌鲁木齐县|