CompletionService,获取最先完成任务的结果

假设我们要向线程池提交一批任务,并获取任务结果。一般的方式是提交任务后,从线程池得到一批 Future 对象集合,然后依次调用其 get() 方法。

这里有个问题:因为我们会要按固定的顺序来遍历 Future 元素,而 get() 方法又是阻塞的,因此如果某个 Future 对象执行时间太长,会使得我们的遍历过程阻塞在该元素上,无法及时从后面早已完成的 Future 当中取得结果。

CompletionService 解决了这个问题,可以获取先执行完毕的任务。

成员属性

public class ExecutorCompletionService<V> implements CompletionService<V> {
    private final Executor executor;
    private final AbstractExecutorService aes;
    private final BlockingQueue<Future<V>> completionQueue;
}

CompletionService 的实现原理也是内部维护了一个阻塞队列,当任务执行结束就把任务的执行结果的 Future 对象加入到阻塞队列中。

下面是一个例子

//它本身不包含线程池,创建一个 CompletionService 需要先创建一个 Executor。
CompletionService<Integer> completionService = new ExecutorCompletionService<>(asyncExecutor);
		int sendCount = 0;
		for (Integet item : list) {
			sendCount++;
			completionService.submit(() -> {
				return item;
			});

		}
		try {
			for (int i = 0; i < sendCount; i++) {
				//阻塞等待最先的返回结果
				completionService.take();
			}
		} catch (InterruptedException e) {
			logger.error("等待返回结果异常", e);
		}

CompletionService 之所以能够做到这点,是因为它没有采取依次遍历 Future 的方式,而是在中间加上了一个结果队列,任务完成后马上将结果放入队列,那么从队列中取到的就是最早完成的结果。

如果队列为空,那么 take() 方法会阻塞直到队列中出现结果为止。此外 CompletionService 还提供一个 poll() 方法,返回值与 take() 方法一样,不同之处在于它不会阻塞,如果队列为空则立刻返回 null。这算是给用户多一种选择。

take()
取走BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到BlockingQueue有新的数据被加入;

poll(long timeout, TimeUnit unit)
从BlockingQueue取出一个队首的对象,如果在指定时间内,队列一旦有数据可取,则立即返回队列中的数据。否则知道时间
参数:
timeout - 放弃前等待多长时间,以 unit 为unit
unit – 一个TimeUnit决定如何解释timeout参数
返回值:
此队列的头部,如果在元素可用之前经过了指定的等待时间,则为null

如果你需要循环提交多个Future任务,并在所有任务完成获取结果,你可以使用Java中的CompletionService来实现。 CompletionService是一个Executor和BlockingQueue的组合,它将已完成任务包装成Future对象并放入队列中。你可以通过轮询队列或使用take()方法来获取完成任务结果。 以下是一个示例代码,演示如何循环提交Future任务获取结果: ```java ExecutorService executor = Executors.newFixedThreadPool(5); CompletionService<Integer> completionService = new ExecutorCompletionService<>(executor); // 提交多个任务 for (int i = 0; i < 10; i++) { final int taskId = i; completionService.submit(() -> { // 执行任务,返回结果 return taskId * 2; }); } // 获取完成任务结果 for (int i = 0; i < 10; i++) { try { Future<Integer> future = completionService.take(); // 阻塞直到有任务完成 Integer result = future.get(); // 获取任务结果 // 使用任务结果 System.out.println("Result: " + result); } catch (InterruptedException e) { // 处理中断异常 e.printStackTrace(); } catch (ExecutionException e) { // 处理计算异常 e.printStackTrace(); } } executor.shutdown(); // 关闭线程池 ``` 在上面的示例中,我们使用循环提交了10个任务,并通过completionService.take()方法阻塞等待任务完成。一旦有任务完成,我们通过future.get()方法获取任务结果,并进行处理。 请注意,在使用CompletionService时,你无需手动创建Future对象,completionService.submit()方法会自动返回Future对象,你只需关注获取结果即可。 希望这个示例对你有帮助!如果还有其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值