CompletionService 使用说明

22 篇文章 2 订阅

读《Java并发编程实战》6.3.5 笔记。

如果向 Executor 提交了一组计算任务,并且希望在计算完成后获得结果,那么可以保留与每个任务的关联的 Future,然后反复使用 get 方法,同时将参数 timeout 指定为 0,从而通过轮询来判断任务是否完成。

这种方法虽然可行,但却有些繁琐。幸运的是,还有一种更好的方法:完成服务CompletionService

说明:

  • CompletionService 将 Executor 和 BlockingQueue 的功能融合在一起。

  • 你可以将 Callable 任务提交给它来执行,然后使用类似于队列操作的 take 和 poll 等方法来获得已完成的结果,而这些结果会在完成时被封装为 Future。

  • ExecutorCompletionService 实现了 CompletionService,并将计算部分委托给一个 Executor。

ExecutorCompletionService

ExecutorCompletionService 的实现非常简单。

  • 在构造函数中创建一个 BlockingQueue 来保存计算完成的结果。

  • 当计算完成时,调用 FutureTask 中的 done 方法。

  • 当提交某个任务时,

    • 该任务将首先包装为一个 QueueingFuture,这是 FutureTask 的一个子类,
    • 然后改写子类的 done 方法,并将结果放入 BlockingQueue中。

源码:

新建:

    public ExecutorCompletionService(Executor executor,
                                     BlockingQueue<Future<V>> completionQueue) {
        if (executor == null || completionQueue == null)
            throw new NullPointerException();
        this.executor = executor;
        this.aes = (executor instanceof AbstractExecutorService) ?
            (AbstractExecutorService) executor : null;
            // 在构造函数中创建一个 BlockingQueue 来保存计算完成的结果
        this.completionQueue = completionQueue;
    }

    public ExecutorCompletionService(Executor executor) {
        if (executor == null)
            throw new NullPointerException();
        this.executor = executor;
        this.aes = (executor instanceof AbstractExecutorService) ?
            (AbstractExecutorService) executor : null;
            // 在构造函数中创建一个 BlockingQueue 来保存计算完成的结果
        this.completionQueue = new LinkedBlockingQueue<Future<V>>();
    }

提交任务:

    public Future<V> submit(Callable<V> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<V> f = newTaskFor(task);
        // 包装一个 QueueingFuture
        executor.execute(new QueueingFuture(f));
        return f;
    }

    private class QueueingFuture extends FutureTask<Void> {
        QueueingFuture(RunnableFuture<V> task) {
            super(task, null);
            this.task = task;
        }
        // 重写 FutureTask 的 done,将结果放入 BlockingQueue中
        protected void done() { completionQueue.add(task); }
        private final Future<V> task;
    }

获取结果:

    public Future<V> take() throws InterruptedException {
        // 利用 BlockingQueue 队列来获取结果
        return completionQueue.take();
    }

完整使用例子

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;
import java.util.concurrent.TimeUnit;

/**
 * {@link ExecutorCompletionService}测试类
 * <p>
 *
 * @author hyl
 * @version v1.0: CompletionServiceTest.java, v 0.1 2020/10/13 18:03 $
 */
public class CompletionServiceTest {

   static   Random random = new Random();

    public static void main(String[] args) throws InterruptedException, ExecutionException {


        ExecutorService executorService = Executors.newFixedThreadPool(10);

        CompletionService<Integer> completionService = new ExecutorCompletionService<>(executorService);


        for (int i = 0; i < 10; i++) {
           completionService.submit(new UpdateRunnable());
        }

        for (int i = 0; i < 10; i++) {
            Future<Integer> take = completionService.take();
            int time = take.get();
            System.out.println(Thread.currentThread().getName()+":run "+time+"s");
        }

        executorService.shutdown();
    }


    static class UpdateRunnable implements Callable<Integer> {

        @Override
        public Integer call() throws Exception {
            int time = random.nextInt(8);
            System.out.println(Thread.currentThread().getName()+":run "+time+"s");
            TimeUnit.SECONDS.sleep(time);
            return time;
        }
    }

}

  • 多个 ExecutorCompletionService 可以共享一个 Executor,因此可以创建一个对于特定计算私有,又能共享也给公共 ExecutorExecutorCompletionService
  • 因此,CompletionService 的作用就相当于一组计算的句柄,这与 Future 作为单个计算的句柄是非常类似的。
  • 通过记录提交给 CompletionService 的任务数量,并计算出已经获取的已完成结果的数量,即使使用一个共享的 Executor,也能知道已经获得了所有任务结果的时间。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值