如果你想Executor提交了一个批处理任务,希望获得结果,只能不断的调用timeout为零的get。幸运的是有一种更好的方法:完成服务(completion service)
CompletionServie整合了Executor和BlockingQueue的功能。可以将Callable的任务提交给它去执行,然后使用类似队列中的take和poll方法,在结果完整可用时获得这个结果。ExecutorCompletionServie是实现CompletionService接口的一个类,并将计算任务委托给一个Executor。
使用CompletionService,我们可以从两个方面提高页面渲染的性能。我们可以每需要下载一个图像就创建一个独立的任务,并在线程池中执行他们,将顺序的下载过程转换为并行的。而且从CompletionService中获取结果,只要任何一个图像下载完成就立刻呈现。
public class Renderer{
private final ExecutorService executor;
Rnderer(ExecutorService executor){
this.executor=executor;
}
void renderPage(CharSequence source){
final List info=scanForImageInfo(source);
CompletionService completion=
new ExecutorCompletionService(executor);
for(final ImageInfo imageInfo:info){
completionService.submit(new Callable(){
public ImageData call(){
return imageInfo.downloadImage();
}
});
}
renderText(source);
try{
for(int t=0,n=info.size();t f=completionService.take();
ImageData imageData=f.get();
renderImage(imageData);
}
}catch(InterruptedException e){
Thread.currentThread().interrupt();
}catch(ExecutionException e){
throw launderThrowable(e.getCause());
}
}
}
多个
ExecutorCompletionService
可以共享单一的
Executor
,因此一个明智的做法是创建一个
ExecutorCompletionService
,他对于特定的计算服务是私有的,然后再共享一个公共的
Executor
。按照这种做法,
CompletionService
所扮演的批处理计算的句柄与
Future
所扮演的单一计算的句柄,在很大程度上是一样的。记录下提交给
CompletionService
的任务个数,然后计算出获得了多少个已完成的结果,这样即使你使用的是共享的
Executor
,也能知晓什么时候批处理任务的所有结果都已经全部获得。
如果有一个活动无法在指定时间完成,那就是失效了,此时应该放弃这个活动。Futrue.get的限时版本符合这个条件,如果超时了会抛出TimeoutException。
第二个问题是当任务超时的时候要能够停止他们。
Page renderPageWithAd() throws InterruptedException{
long endNanos=System.nanoTime()+TIME_BUDGET;
Future f=exec.submit(new FetchAdTask());
Page page=renderPageBody();
Ad ad;
try{
long timeLeft=endNanos-System.nanoTime();
ad=f.get(timeLeft,NANOSECONDS);
}catch(ExecutionException e){
ad=DEFAULT_AD;
}catch(TimeoutException e){
ad=DEFAULT_AD;
f.cancel(true);
}
page.setAd(ad);
return page;
}
在预定时间内请求旅游报价
private class QuoteTask implements Callable{
private final TravelCompany company;
private final TravelInfo travelInfo;
public TravelQuote call()throws Exception{
return company.solicitQuote(travelInfo);
}
}
public List getRankedTravelQuotes(
TravelInfo travelInfo,Set companies,
Comparator ranking,long time,TimeUnit unit){
List tasks=new ArrayList();
for(TravelCompany company:companies){
tasks.add(new QuoteTask(company,travelInfo));
}
List> futures = exec.invokeAll(task,time,unit);
List quotes = new ArrayList(tasks.size));
Iterator taskIter=tasks.iterator();
for(Future f:futures){
QuoteTask task=taskIter.next();
try{
quotes.add(f.get());
}catch(ExecutionException e){
quotes.add(task.getFailureQuote(e.getCause()));
}catch(CancellationException e){
quotes.add(task.getTimeoutQuote(e));
}
}
Collections.sore(quotes,ranking);
return quotes;
}
关于任务和线程的停止,Java没有提供任何机制来安全的迫使线程停止工作,她提供中断——一个协作机制使一个线程能够要求另一个线程停止当前的工作。
public class PrimeGenerator implments Runnable{
private final List primes=new ArrayList();
private volatile boolean cancelled;
public void run(){
BigInteger p=BigInteger.ONE;
while(!cancelled){
p=p.nextProbablePrime();
synchronized(this){
primes.add(p);
}
}
}
public void cancel(){
cancelled=true;
}
public synchronized List get(){
return new ArrayList(primes);
}
}
List aSecondOfPrimes()throws InterruptedException{
PrimeGenerator generator=new PrimeGenerator();
new Thread(generator).start();
try{
TimeUnit.SECONDS.sleep(1);
}finally{
generator.cancel();
}
return generator.get();
}
class BrokenPrimeProducer extends Thread{
private final BlockingQueue queue;
private volatile boolean cancelled=false;
BrokenPrimeProducer(BlockingQueue queue){
this.queue=queue;
}
public void run(){
try{
BigInteger p=BigInteger.ONE;
while(!cancelled){
queue.put(p=p.nextProbablePrime());
}catch(InterruptedException consumed){
}
}
}
public void cancel(){
cancelled=true;
}
}
void consumePrimes()throws InterruptedException{
BlockingQueue primes=...
BrokenPrimeProducer producer=new BrokenPrimeProducer(primes);
producer.start();
try{
while(needMorePrimes()){
consume(primes.take());
}
}finally{
producer.cancel();
}
}
我们需要制定中断策略,一个中断策略决定线程如何应对中断请求——当发现请求时会做什么,哪些工作单元对于终端来说是原子操作以及在多快的时间里响应中断。
Thread中静态的interrupted应该小心使用,因为他会清除并发线程的终端状态。
public Task getNextTask(BlockingQueue queue){
boolean interrupted=false;
try{
while(true){
try{
return queue.take();
}catch(InterruptedException e){
interrupted=true;
}
}
}finally{
if(interrupted){
Thread.currentThread().interrupt();
}
}
}
private static final ScheduledExecutorService cancelExec=...
public static void timedRun(Runnable r,long timeout,TimeUnit unit){
final Thread taskThread=Thread.currentThread();
cancelExec.schedule(new Runnable(){
public void run(){
taskThread.interrupt();
}
});
r.run();
}
public static void timeRun(final Runnable r,long timeout,TimeUnit unit){
class RethrowableTask implements Runnable{
private volatile Throwable t;
public void run(){
try{
r.run();
}catch(Throwable t){
this.t=t;
}
}
}
}
RethrowableTask task=new RethrowableTask();
final Thread taskThread=new Thread(task);
taskThread.start();
cancelExec.schedule(new Runnable(){
public void run(){
taskThread.interrupt();
}
},timeout,unit);
taskThread.join(unit.toMillis(timeout));
task.rethrow();