【Java多線程】ExecutorService與ExecutorCompletionService

  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的源碼:


   成員變量如下:

wKioL1dfvU6wWyrxAAAv-TgEQAc139.jpg-wh_50

   executor:ExecutorService類,任務並行執行器

   completionQueue:就是保存執行結果的阻塞隊列BlockingQueue


wKioL1dfvpiyDtmzAAAnFl_RQXU002.png-wh_50

   submit方法:底層依舊使用ExecutorService來併發執行任務,只不過是多了個功能【把執行完畢的任務放到complectionQueue隊列中】


wKiom1dfvlvAmAxeAAAaV7kvm6U428.jpg-wh_50

   task方法:從complectionQueue隊列中獲取一個元素,如果沒有元素,則阻塞,直到隊列中有元素位置,這驗證了我們之前的說法。


  總結:總的來說,ExecutorComplectionService其實就是 ExecutorService 和 BlockingQueue的結合。


  

   

   


    

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章