ExecutorService與ExecutorCompletionService都是java.util.concurrent包的併發處理類,總的來說,ExecutorCompletionService是ExecutorService的功能增強版,ExecutorCompletionService以BlockingQueue<Future<V>>來存放已經完成的任務。
也就是說,優先完成的任務會優先存放在BlockingQueue<Future<V>>隊列中,所以我們能及時的拿到最優先的處理結果。
讓我們先看看ExecutorService的測試代碼,共4個任務,我們刻意讓第1個任務的執行時間最長,依次遞減,代碼如下:
import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class TestExecutorService { public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService es = Executors.newFixedThreadPool(4); //第一個任務 Callable<Integer> task1 = new Callable<Integer>() { public Integer call() throws Exception { //耗時最長, 4秒 Thread.sleep(4000); return 1; } }; //第二個任務 Callable<Integer> task2 = new Callable<Integer>() { public Integer call() throws Exception { Thread.sleep(3000); return 2; } }; //第三個任務 Callable<Integer> task3 = new Callable<Integer>() { public Integer call() throws Exception { Thread.sleep(2000); return 3; } }; //第四個任務 Callable<Integer> task4 = new Callable<Integer>() { public Integer call() throws Exception { Thread.sleep(1000); return 4; } }; Future<Integer> result1 = es.submit(task1); Future<Integer> result2 = es.submit(task2); Future<Integer> result3 = es.submit(task3); Future<Integer> result4 = es.submit(task4); System.out.println("第1個任務等待中..."); System.out.println("第1個任務完成:【" + result1.get() + "】"); System.out.println("第2個任務等待中..."); System.out.println("第2個任務完成:【" + result2.get() + "】"); System.out.println("第3個任務等待中..."); System.out.println("第3個任務完成:【" + result3.get() + "】"); System.out.println("第4個任務等待中..."); System.out.println("第4個任務完成:【" + result4.get() + "】"); } }
輸出結果:
第1個任務等待中... 第1個任務完成:【1】 第2個任務等待中... 第2個任務完成:【2】 第3個任務等待中... 第3個任務完成:【3】 第4個任務等待中... 第4個任務完成:【4】
PS:Future.get()方法會造成阻塞,直到任務執行完畢爲止。
運行代碼可見,result1.get()阻塞了4秒後完成任務,輸出結果,而緊隨的result2.get(),result3.get(),result4.get()沒有阻塞,立馬輸出結果。那是因爲在result1.get()執行完畢時,其餘3個任務早已執行完畢等待抓取結果了。所以,使用上述方法並不能得知哪個任務是最先返回結果的。
接下來,讓我們看看ExecutorCompletionService的代碼:
import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class TestExecutorComplectionService { public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService es = Executors.newFixedThreadPool(4); ExecutorCompletionService<Integer> ecs = new ExecutorCompletionService<Integer>(es); //第一個任務 Callable<Integer> task1 = new Callable<Integer>() { public Integer call() throws Exception { //耗時最長, 4秒 Thread.sleep(4000); return 1; } }; //第二個任務 Callable<Integer> task2 = new Callable<Integer>() { public Integer call() throws Exception { Thread.sleep(3000); return 2; } }; //第三個任務 Callable<Integer> task3 = new Callable<Integer>() { public Integer call() throws Exception { Thread.sleep(2000); return 3; } }; //第四個任務 Callable<Integer> task4 = new Callable<Integer>() { public Integer call() throws Exception { Thread.sleep(1000); return 4; } }; ecs.submit(task1); ecs.submit(task2); ecs.submit(task3); ecs.submit(task4); for(int i = 0; i < 4; i++) { Future<Integer> result = ecs.take(); System.out.println("輸出結果:【" + result.get() + "】"); } } }
輸出結果:
輸出結果:【4】 輸出結果:【3】 輸出結果:【2】 輸出結果:【1】
可見,第4個任務task4執行的時間是最短的,首先輸出結果。
下面讓我們剖析一下ExecutorComplectionService的源碼:
成員變量如下:
executor:ExecutorService類,任務並行執行器
completionQueue:就是保存執行結果的阻塞隊列BlockingQueue
submit方法:底層依舊使用ExecutorService來併發執行任務,只不過是多了個功能【把執行完畢的任務放到complectionQueue隊列中】
task方法:從complectionQueue隊列中獲取一個元素,如果沒有元素,則阻塞,直到隊列中有元素位置,這驗證了我們之前的說法。
總結:總的來說,ExecutorComplectionService其實就是 ExecutorService 和 BlockingQueue的結合。