异步多线程----控制任务组

  有时,使用执行器有更深层次的原因----控制一组相关任务。例如,可以在执行器中使用shutdownNow方法取消所有的任务。

ExecutorService

//执行给定的任务,返回其中一个任务的结果
T invokeAny(Collection> tasks)
//执行给定的任务,返回所有任务的结果
List> invokeAll(Collection> tasks)

  invokeAny方法提交所有对象到一个Callable对象的集合中,并返回某个已经完成了的任务结果。无法知道返回的究竟是哪个任务的结果。
  invokeAll方法提交所有对象到一个Callable对象的集合中,并返回一个Future对象的列表,代表所有任务的解决方案。当计算结果可获得时,可以像下面这样对结果进行处理:

List> tasks = ...;
List> results = executor.invokeAll(tasks);
for (Future result : results)
    processFuther(result.get());

  上面这种处理方法的缺点是,如果第一个任务恰巧花去了很多时间,则可能不得不进行等待(返回的Future列表按任务添加顺序进行排列)。将结果按可获得的顺序保存起来更有实际意义。可以使用ExecutorCompletionService,来进行排列(返回的Future列表按任务完成顺序排列)

  用常规的方法获得一个执行器。然后,构建一个ExecutorComPletionService提交任务给CompletionService(完成服务)。该服务管理Future对象的阻塞队列,其中包含已经提交的任务执行结果(当这些结果成为可用时)。相比前面的计算,一个更有效的组织形式如下:

ExecutorCompletionService service = new ExecutorCompletionService<>(executor);
for (Callable task : tasks) service.submit(task);
for (int i = 0; i < tasks.size(); i++)
     //先完成的先执行
     processFurther(service.take().get());
ExecutorCompletionService

//构建一个执行器完成服务来收集给定执行器的结果
ExecutorCompletionService(Executor e)

//提交一个任务给底层的执行器
Future submit(Callable task)

//获得并移除下一个已完成任务的Future,如果没有任何已完成的结果可以使用,则阻塞。
Future take()

////获得并移除下一个已完成任务的Future,如果没有任何已完成的结果可以使用,则返回null。
Future poll()

  ExecutorService和CompletionService,这两者最主要的区别: 在于submit的task不一定是按照加入自己维护的list顺序完成的。从list中遍历的每个Future对象并不一定处于完成状态,这时调用get()方法就会被阻塞住,如果系统是设计成每个线程完成后就能根据其结果继续做后面的事,这样对于处于list后面的但是先完成的线程就会增加了额外的等待时间。
  而CompletionService的实现是维护一个保存Future对象的BlockingQueue。只有当这个Future对象状态是结束的时候,才会加入到这个Queue中,take()方法其实就是Producer-Consumer中的Consumer。它会从Queue中取出Future对象,如果Queue是空的,就会阻塞在那里,直到有完成的Future对象加入到Queue中。
  所以,先完成的必定先被取出。这样就减少了不必要的等待时间(ExecutorService的get不一定能马上得到对象,而CompletionService先take再get,可以马上获得对象)

你可能感兴趣的:(异步多线程----控制任务组)