软件编程
位置:首页>> 软件编程>> java编程>> Java多线程 CompletionService

Java多线程 CompletionService

作者:冬日毛毛雨  发布时间:2022-08-19 18:51:32 

标签:Java,多线程,CompletionService
目录
  • 1 CompletionService介绍

  • 2 CompletionService源码分析

  • 3 CompletionService实现任务

  • 4 CompletionService总结

1 CompletionService介绍

CompletionService用于提交一组Callable任务,其take方法返回已完成的一个Callable任务对应的Future对象。
如果你向Executor提交了一个批处理任务,并且希望在它们完成后获得结果。为此你可以将每个任务的Future保存进一个集合,然后循环这个集合调用Futureget()取出数据。幸运的是CompletionService帮你做了这件事情。
CompletionService整合了ExecutorBlockingQueue的功能。你可以将Callable任务提交给它去执行,然后使用类似于队列中的take和poll方法,在结果完整可用时获得这个结果,像一个打包的Future
CompletionService的take返回的future是哪个先完成就先返回哪一个,而不是根据提交顺序。

2 CompletionService源码分析

首先看一下 构造方法:


  public ExecutorCompletionService(Executor executor) {
       if (executor == null)
           throw new NullPointerException();
       this.executor = executor;
       this.aes = (executor instanceof AbstractExecutorService) ?
           (AbstractExecutorService) executor : null;
       this.completionQueue = new LinkedBlockingQueue<Future<V>>();
   }

构造法方法主要初始化了一个阻塞队列,用来存储已完成的task任务。

然后看一下 completionService.submit 方法:


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

public Future<V> submit(Runnable task, V result) {
       if (task == null) throw new NullPointerException();
       RunnableFuture<V> f = newTaskFor(task, result);
       executor.execute(new QueueingFuture(f));
       return f;
   }

可以看到,callable任务被包装成QueueingFuture,而 QueueingFutureFutureTask的子类,所以最终执行了FutureTask中的run()方法。

来看一下该方法:


public void run() {
//判断执行状态,保证callable任务只被运行一次
   if (state != NEW ||
       !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                    null, Thread.currentThread()))
       return;
   try {
       Callable<V> c = callable;
       if (c != null && state == NEW) {
           V result;
           boolean ran;
           try {
           //这里回调我们创建的callable对象中的call方法
               result = c.call();
               ran = true;
           } catch (Throwable ex) {
               result = null;
               ran = false;
               setException(ex);
           }
           if (ran)
           //处理执行结果
               set(result);
       }
   } finally {
       runner = null;
       // state must be re-read after nulling runner to prevent
       // leaked interrupts
       int s = state;
       if (s >= INTERRUPTING)
           handlePossibleCancellationInterrupt(s);
   }
}

可以看到在该 FutureTask 中执行run方法,最终回调自定义的callable中的call方法,执行结束之后,

通过 set(result) 处理执行结果:


   /**
    * Sets the result of this future to the given value unless
    * this future has already been set or has been cancelled.
    *
    * <p>This method is invoked internally by the {@link #run} method
    * upon successful completion of the computation.
    *
    * @param v the value
    */
   protected void set(V v) {
       if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
           outcome = v;
           UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
           finishCompletion();
       }
   }

继续跟进finishCompletion()方法,在该方法中找到 done()方法:

protected void done() { completionQueue.add(task); }

可以看到该方法只做了一件事情,就是将执行结束的task添加到了队列中,只要队列中有元素,我们调用take()方法时就可以获得执行的结果。
到这里就已经清晰了,异步非阻塞获取执行结果的实现原理其实就是通过队列来实现的,FutureTask将执行结果放到队列中,先进先出,线程执行结束的顺序就是获取结果的顺序。

CompletionService实际上可以看做是ExecutorBlockingQueue的结合体。CompletionService在接收到要执行的任务时,通过类似BlockingQueue的put和take获得任务执行的结果。CompletionService的一个实现是ExecutorCompletionServiceExecutorCompletionService把具体的计算任务交给Executor完成。

在实现上,ExecutorCompletionService在构造函数中会创建一个BlockingQueue(使用的基于链表的 * 队列LinkedBlockingQueue),该BlockingQueue的作用是保存Executor执行的结果。当计算完成时,调用FutureTask的done方法。当提交一个任务到ExecutorCompletionService时,首先将任务包装成QueueingFuture,它是FutureTask的一个子类,然后改写FutureTask的done方法,之后把Executor执行的计算结果放入BlockingQueue中。

QueueingFuture的源码如下:


   /**
    * FutureTask extension to enqueue upon completion
    */
   private class QueueingFuture extends FutureTask<Void> {
       QueueingFuture(RunnableFuture<V> task) {
           super(task, null);
           this.task = task;
       }
       protected void done() { completionQueue.add(task); }
       private final Future<V> task;
   }

3 CompletionService实现任务


public class CompletionServiceTest {
   public static void main(String[] args) {

ExecutorService threadPool = Executors.newFixedThreadPool(10);
       CompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(threadPool);
       for (int i = 1; i <=10; i++) {
           final int seq = i;
           completionService.submit(new Callable<Integer>() {
               @Override
               public Integer call() throws Exception {

Thread.sleep(new Random().nextInt(5000));

return seq;
               }
           });
       }
       threadPool.shutdown();
       for (int i = 0; i < 10; i++) {
           try {
               System.out.println(
                       completionService.take().get());
           } catch (InterruptedException e) {
               e.printStackTrace();
           } catch (ExecutionException e) {
               e.printStackTrace();
           }
       }

}
}

7
3
9
8
1
2
4
6
5
10

4 CompletionService总结

相比ExecutorServiceCompletionService可以更精确和简便地完成异步任务的执行
CompletionService的一个实现是ExecutorCompletionService,它是ExecutorBlockingQueue功能的融合体,Executor完成计算任务,BlockingQueue负责保存异步任务的执行结果
在执行大量相互独立和同构的任务时,可以使用CompletionService
CompletionService可以为任务的执行设置时限,主要是通过BlockingQueuepoll(long time,TimeUnit unit)为任务执行结果的取得限制时间,如果没有完成就取消任务

来源:https://juejin.cn/post/7018423693793558558

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com