completion services是一个java.util.concurrent.CompletionService<V>接口的应用,这个是从同步任务和从任务完成的返回结果中解耦。V是任务返回的类型。
一个生产者提交一个任务执行(通过一个工作线程),通过请求一个submit()方法:一个方法接受可调用的参数和一个方法接受运行中的参数随后结果返回任务的完成。为了任务的完成,你可以从池中请求poll()的方法或请求阻塞的take()方法。
一个消费者从请求take()的方法中获取一个完成的任务。这个方法会阻塞直到一个任务的完成。它会返回一个Future<V>的对象,代表任务完成。你可以请求Future<V>的get()方法去包含这个结果。
与之关联的CompletionService<V>,Java7介绍了java.util.concurrent.ExecutorCompletionService<V>类,这个类提供任务执行通过一个提供的执行器。这个类确保,当提交的任务已经完成,它们将会放置在队列take()中。
为了演示CompletionService和ExecutorCompletionService,我重新写了一个应用去计算Euler的数,这个在第五章节已经写过。Listing 8-8出现的资源代码是一个新的应用,通过两个随时可以调用任务去精确计算数字。
Listing 8-8
package com.owen.thread.chapter8;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class CSDemo
{
public static void main(String[] args) throws Exception
{
ExecutorService es = Executors.newFixedThreadPool(10);
CompletionService<BigDecimal> cs = new ExecutorCompletionService<BigDecimal>(
es);
cs.submit(new CalculateE(17));
cs.submit(new CalculateE(170));
Future<BigDecimal> result = cs.take();
System.out.println(result.get());
System.out.println();
result = cs.take();
System.out.println(result.get());
es.shutdown();
}
}
class CalculateE implements Callable<BigDecimal>
{
final int lastIter;
public CalculateE(int lastIter)
{
this.lastIter = lastIter;
}
@Override
public BigDecimal call()
{
MathContext mc = new MathContext(100, RoundingMode.HALF_UP);
BigDecimal result = BigDecimal.ZERO;
for (int i = 0; i <= lastIter; i++)
{
BigDecimal factorial = factorial(new BigDecimal(i));
BigDecimal res = BigDecimal.ONE.divide(factorial, mc);
result = result.add(res);
}
return result;
}
private BigDecimal factorial(BigDecimal n)
{
if (n.equals(BigDecimal.ZERO))
return BigDecimal.ONE;
else
return n.multiply(factorial(n.subtract(BigDecimal.ONE)));
}
}
Listing8-8出现两个类:CSDemo和CalculateE.CSDemo运行这个应用和CaculateE描述一个Euler数字计算的任务。
CSDemo的main()方法第一次创建一个执行器服务,它将会执行一个任务。它创建一个completion service去完成任务。两个计算任务随后提交到completion service,这将会返回每一个同步的任务。对于每一个任务,completion service的take()方法被请求,并且返回一个任务的结果,谁调用get()的方法将会包含任务的结果,它会输出信息。
Calculate包含的代码与第五章节的相似,唯一不一样的是改变一个LASTITER不间断地变化到lastIter的变量,记录着最后一个迭代器的执行(和确定精度的位数)。
执行上面的代码,你可能得到如下的结果:
2.718281828459045070516047795848605061178979635251032698900735004065225042504843314055887974344245741730039454062711
2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274638961162816541248130487298653803083054255628382459134600326751445819115604942105262868564884769196304284703491677706848122126664838550045128841929851772268853216753574895628940347880297133296754744949375835005542283846314528419863840501124972044069282255484327668062074149805932978161481951711991448146506
Note 如果你关于执行服务(executorservice)和完成服务(completion service)存在疑惑,思考这个,一个执行服务,在写所写的代码需要提交任务,你需要去写代码去有效地获取任务的结果返回;对于一个完成服务,这项目工作是完全自动的。其它方式去看这个两的区别,一个执行服务提供进入队列给任务,和提供工作线程;而一个完成服务提供一个进入队列人任务、工作线程和一个输出队列去储存任务结果。