多執行緒 如何確定所有任務都執行完成了?

2021-09-02 21:32:53 字數 3712 閱讀 2741

--------------------20180502更新-----------------------------

今天學習到了乙個比較強大的類:executorcompletionservice,它是將 executor和blockqueue結合的jdk類,其實現的主要目的是:提交任務執行緒,每乙個執行緒任務直線完成後,將返回值放在阻塞佇列中,然後可以通過阻塞佇列的take()方法返回 對應執行緒的執行結果!!

所以還可以這樣寫:

executorcompletionservicecompletionservice = new executorcompletionservice(executors.newfixedthreadpool(5));

for(int i=0; i<10; i++)

try

}} catch (exception e)

system.out.println("---------->結束");

同樣可以達到阻塞的效果!(注:其中有用到jdk1.8的lambda表示式~)

之前我有寫過一篇部落格,是關於多執行緒寫同乙個sheet檔案的。類似的場景很多,當我們想用多執行緒提高效率時,面臨的關鍵問題就是執行緒安全和確定所有任務都完成。執行緒安全的問題那篇部落格有說,就是確保對公共資源的寫操作是安全的,比如list的add操作採用synchronized來包裝或直接採用執行緒安全的集合;sheet的addrow加鎖等… 而本篇的重點是「如何確保所有任務都完成,才能進行下一步?」。

先來看現象:

public static void m() 

}).start();

}system.out.println("---------->結束");

}

這段**建立了10個執行緒,每個執行緒的任務會列印當前執行緒的名字,執行後發現可能出現以下結果(順序不一定是下面這樣):

thread-1------>1

---------->結束

thread-0------>0

thread-2------>2

thread-4------>4

thread-3------>3

thread-5------>5

thread-6------>6

thread-7------>7

thread-8------>8

thread-9------>9

會發現「---------->結束」沒有在所有執行緒都執行完就列印出來了,對映到實際場景就是用多執行緒去幫我們幹活,還沒幹完呢就直接下一步了,如此的話沒有實際意義(除非這個多執行緒的任務是非同步的,其他邏輯不需要等待它完成才能進行)。

我們知道,多執行緒執行任務可以用原始的執行緒提交(上述**),也可以用執行緒池(比較推薦這種方式,便於對執行緒進行管理)。為了解決上述問題,可以用countdownlatch計數器,計數器的初始大小要跟任務數的大小一致(跟執行緒數無關),每執行一次任務,計數器減一(countdown),await()方法會一直阻塞主線程,直到計數器的值減為0,才會釋放鎖,如此便可以達到確保所有任務都完成才繼續下一步的效果。

先用原始執行緒結合計數器的方式來試試效果:

public static void m1() 

}).start();

}try catch (interruptedexception e)

system.out.println("---------->結束");

}

無論執行多少次,會發現"---------->結束"始終會在多執行緒所有任務都執行完畢後列印,比如某次結果:

thread-0------>0

thread-1------>1

thread-2------>2

thread-3------>3

thread-4------>4

thread-7------>7

thread-8------>8

thread-9------>9

thread-5------>5

thread-6------>6

---------->結束

再用執行緒池結合計數器的方式來嘗試:

public static void m2() 

});}

try catch (interruptedexception e)

system.out.println("---------->結束");

}

同樣地,無論執行多少次,"---------->結束"都會在所有任務都完成以後再進行!比如某次列印結果為:

pool-1-thread-2------>1

pool-1-thread-2------>5

pool-1-thread-3------>2

pool-1-thread-1------>0

pool-1-thread-1------>8

pool-1-thread-1------>9

pool-1-thread-3------>7

pool-1-thread-4------>3

pool-1-thread-2------>6

pool-1-thread-5------>4

---------->結束

這充分印證了countdownlatch計數器的強大!下面我們再看乙個比較容易忽略的方式:

public static void m3() 

});list.add(future);

}try

} catch (interruptedexception e) catch (executionexception e)

system.out.println("---------->結束");

}

callable不用多說,它可以表示乙個有返回值的執行緒,future則用於接收返回的結果。future的get方法具有阻塞作用,它會一直阻塞直至獲取到結果。callable&future一般都是結合執行緒池來使用。

執行多次,也會發現"---------->結束"總是在最後執行的,同樣達到了目的。

說到執行緒池管理執行緒,需要注意的是比如:

executors.newfixedthreadpool(40)
實際上是new了乙個corepoolsize=maximumpoolsize的特殊情況的執行緒池:

public static executorservice newfixedthreadpool(int nthreads)
executors提供的靜態方法建立執行緒池,內部都是去構造乙個threadpoolexecutor,只是不同型別的執行緒池,

corepoolsize和maximumpoolsize的大小關係不同,還有採用的任務佇列的型別也不同。

關於多執行緒和執行緒池的一些知識補充,見以下手工筆記:

1.一些多執行緒的基本概念:

2、執行緒池型別以及提交執行緒的過程:

多執行緒 等待所有任務執行完成

在多執行緒中,有時候需要等待所有執行緒執行完成後才讓繼續往下執行,如查詢結果彙總等。下面列舉兩種等待方式 方式一 利用 countdownlatch 類完成。示例 按任務總量建立計數器 final countdownlatch countdownlatch new countdownlatch so...

多執行緒主線程等待子執行緒都執行完成才執行

1.建立countdownlatch物件,設定子執行緒的數量 countdownlatch countdownlatch new countdownlatch size 其中size就是子執行緒的數量 2.開啟執行緒執行方法 壓縮生成.tbwj file bidfile ziputil.zip te...

springboot 多執行緒執行定時任務

1.建立專案 網上有很多,這裡省略。2.匯入依賴 org.springframework.boot spring boot starter web org.springframework.boot spring boot starter org.projectlombok lombok true o...