Java多线程 CompletionService

网友投稿 425 2022-09-19


Java多线程 CompletionService

目录1 CompletionService介绍2 CompletionService源码分析3 CompletionService实现任务4 CompletionService总结

1 CompletionService介绍

CompletionService用于提交一组Callable任务,其take方法返回已完成的一个Callable任务对应的Future对象。

如果你向Executor提交了一个批处理任务,并且希望在它们完成后获得结果。为此你可以将每个任务的Future保存进一个集合,然后循环这个集合调用Future的get()取出数据。幸运的是CompletionService帮你做了这件事情。

CompletionService整合了Executor和BlockingQueue的功能。你可以将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>();

}

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

然后看一下 completionService.submit 方法:

public Future submit(Callable task) {

if (task == null) throw new NullPointerException();

RunnableFuture f = newTaskFor(task);

executor.execute(new QueueingFuture(f));

return f;

}

public Future submit(Runnable task, V result) {

if (task == null) throw new NullPointerException();

RunnableFuture f = newTaskFor(task, result);

executor.execute(new QueueingFuture(f));

return f;

}

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

来看一下该方法:

public void run() {

//判断执行状态,保证callable任务只被运行一次

if (state != NEW ||

!UNSAFE.compareAndSwapObject(this, runnerOffset,

null, Thread.currentThrePKdOdad()))

return;

try {

Callable c = callable;

if (c != null && state == NEW) {

V result;

boolean ran;

try {

//这里回调我们创建的callable对象中的call方法

result = c.call();

ran = true;

} catch (Throwable ex) {

result = null;

http:// ran = false;

setException(ex);

PKdOd }

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.

*

*

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实际上可以看做是Executor和BlockingQueue的结合体。CompletionService在接收到要执行的任务时,通过类似BlockingQueue的put和take获得任务执行的结果。CompletionService的一个实现是ExecutorCompletionService,ExecutorCompletionService把具体的计算任务交给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 {

QueueingFuture(RunnableFuture task) {

super(task, null);

this.task = task;

}

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

private final Future task;

}

3 CompletionService实现任务

public class CompletionServiceTest {

public static void main(String[] args) {

ExecutorService threadPool = Executors.newFixedThreadPool(10);

CompletionService completionService = new ExecutorCompletionService(threadPool);

for (int i = 1; i <=10; i++) {

final int seq = i;

completionService.submit(new Callable() {

@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总结

相比ExecutorService,CompletionService可以更精确和简便地完成异步任务的执行

CompletionService的一个实现是ExecutorCompletionService,它是Executor和BlockingQueue功能的融合体,Executor完成计算任务,BlockingQueue负责保存异步任务的执行结果

在执行大量相互独立和同构的任务时,可以使用CompletionService

CompletionService可以为任务的执行设置时限,主要是通过BlockingQueue的poll(long time,TimeUnit unit)为任务执行结果的取得限制时间,如果没有完成就取消任务


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:路由策略调用工具—filter-policy(策略路由插件)
下一篇:路由策略调用工具—import-router(支持策略路由的路由器)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~