线程池中CompletionService的应用

当使用ExecutorService启动了多个Callable后,每个Callable会产生一个Future,我们需要将多个Future存入一个线性表,用于之后处理数据。当然,还有更复杂的情况,有5个生产者线程,每个生产者线程都会创建任务,所有任务的Future都存放到同一个线性表中。另有一个消费者线程,从线性表中取出Future进行处理。

CompletionService正是为此而存在,它是一个更高级的ExecutorService,它本身自带一个线程安全的线性表,无需用户额外创建。它提供了2种方法从线性表中取出结果,poll()是非阻塞的,若目前无结果,返回一个null,线程继续运行不阻塞。take()是阻塞的,若当前无结果,则线程阻塞,直到产生一个结果,被取出返回,线程才继续运行。

public class Test {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		ExecutorService executor = Executors.newCachedThreadPool();
		CompletionService<Integer> comp = new ExecutorCompletionService<>(executor);
		for(int i = 0; i<5; i++) {
			comp.submit(new Task());
		}
		executor.shutdown();
		int count = 0, index = 1;
		while(count<5) {
			Future<Integer> f = comp.poll();
			if(f == null) {
				System.out.println(index + " 没发现有完成的任务");
			}else {
				System.out.println(index + "产生了一个随机数: " + f.get());
				count++;
			}
			index++;
			TimeUnit.MILLISECONDS.sleep(500);
		}
	}
}

class Task implements Callable<Integer> {

	@Override
	public Integer call() throws Exception {
		Random rand = new Random();
		TimeUnit.SECONDS.sleep(rand.nextInt(7));
		return rand.nextInt();
	}
	
}

实际运用小案例:

模拟页面渲染

package com.thread;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
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;


/**
 * CompletionService的运用:当Executor遇见BlockingQueue时使用。
 * 使用场景:
 * 如果向Executor提交了一个批处理任务,并且希望在他们完成后获得结果。
 * 
 * CompletionService整合了Executor和BlockingQueue的功能,你可以将Callable任务提交给它执行,
 * 然后使用类似队列中的take和poll方法,在结果完整可用时(只是等待任意一个future的返回值),获得这个结果。就像一个大包的Future。
 * 
 * completionService.take()的说明是:检索并移除已完成的任务,如果没有任何一个任务完成的,则继续等待
 * 
 * 从案例的结果可以看出,每当图片下载完毕后,就会执行渲染操作。
 * take方法只是检索completionService中所有future,看是否有执行完的任务,并获得结果。
 * 
 * @author hadoop
 *
 */
public class CompletionThread {
	static  ExecutorService mExecutor = Executors.newFixedThreadPool(5);
	static int totalTimeDownPhoto =0;
	
	/**
	 * 模拟页面渲染场景
	 */
	static  void  renderPage(){
		  final  List<String> info = new ArrayList<String>();
		  for (int i = 0; i < 20; i++) {
			  info.add("图片" + i);
		  }
		  CompletionService<String>  completionService = new ExecutorCompletionService<String>(mExecutor);
		  
		  /**
		   * 开启多线程处理下载图片的任务
		   */
		  for(final String str : info){
			  completionService.submit(new Callable<String>() {
				@Override
				public String call() throws Exception {
					//下载图片download(str)
					int randomTime = new Random().nextInt(9) + 1;//限制耗时不会出现0s,不会大于10s
					Thread.sleep(randomTime*1000);
					System.out.println("下载" + str +"耗费了" + randomTime + "s");
					computeTime(randomTime);
					return str;
				}
			 });
		  }
		  
		 
		  
	    try {
		    System.out.println("处理文字渲染的逻辑");
		    int randomTime = new Random().nextInt(9) + 1;
			Thread.sleep(1000*randomTime);
			computeTime(randomTime);
			System.out.println("处理文字渲染的逻辑耗费了" + randomTime + "s");
			
			/**
			 * 如果渲染图片耗时也比较久,也可以使用多线程。这里只是模拟,没有使用多线程处理渲染图片的过程
			 */
			for (int i = 0; i < info.size(); i++) {
				 //take检索并移除已完成的任务,如果没有任何一个任务完成的,则继续等待
				  Future<String> f = completionService.take();
				 //处理渲染图片的逻辑
				  randomTime = new Random().nextInt(3) + 1;
				  Thread.sleep(1000*randomTime);
				  computeTime(randomTime);
				  System.out.println("渲染"+f.get() +"耗时"+randomTime+"s"); 
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
	    
	    /**
	     * 只有执行了shutdown方法,执行isTerminated才有效。否则isTerminated一直为ture
	     */
	    mExecutor.shutdown();
	    while(true){
			if(mExecutor.isTerminated()){
				System.out.println("所有任务都执行完了,关闭线程池");
				break;
			}
		}
	}
	
	/**
	 * 统计下载图片所花费的总时间
	 * @param randomTime
	 */
	static void computeTime(int randomTime){
		synchronized (mExecutor) {
			totalTimeDownPhoto += randomTime;
		}
	}
	
	
	public static void main(String[] args) {
		long start = System.currentTimeMillis();
		renderPage();
		long end = System.currentTimeMillis();
		System.out.println("渲染页面总耗时:"+(end - start));
		System.out.println("下载每张图片,渲染每张图片以及渲染文字的合计耗时是:"+ totalTimeDownPhoto);
		
		int saveTime = (int) (totalTimeDownPhoto - (end - start)/1000);
		System.out.println("总节约时间:"+ saveTime+"s");
		
	}
	
	
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

斗者_2013

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值