欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Callable、Future和CompletionService

程序员文章站 2022-04-20 10:58:26
...

在整理执行器Executor之前,需要提到这样几个类/接口,这些类在使用执行器实现并发开发的时候是比较有用的,他们就是Callable、Future和CompletionService。

0. Callable

在最开始整理Java多线程并发的文章时就提到过了,Runnable是在JDK1.0中就有的。我们再来回头看下java.lang.Runnable:

 

[java] view plaincopyCallable、Future和CompletionService
            
    
    博客分类: Thread CallableFutureCompletionService Callable、Future和CompletionService
            
    
    博客分类: Thread CallableFutureCompletionService 
 
  1. public interface Runnable {  
  2.    public abstract void run();  
  3. }  

 

可以看到其只有一个public void修饰的run()方法,注意是返回类型为void方法,这就意味着对这个方法的调用不会有是没有返回结果的,但很多场景一个任务完成执行,我们是需要知道它的结果的。为了弥补这个不足, 从JDK1.5开始,在java.util.concurrent包中就有了Callable这个接口。

 

[java] view plaincopyCallable、Future和CompletionService
            
    
    博客分类: Thread CallableFutureCompletionService Callable、Future和CompletionService
            
    
    博客分类: Thread CallableFutureCompletionService 
 
  1. public interface Callable<V> {  
  2.     V call() throws Exception;  
  3. }  

 

注意到,其中call()方法除了有返回结果以外,比起run()还有异常抛出,这个使用时是要注意的。

在JavaSE5之后,执行器Executor是JDK提供给我们的良好工具,在ExecutorService中也有了支持Callable的submit()方法,那么对于其call()的执行结果我们如何来取呢,这就要提到另一个类——java.util.concurrent.Future。

1. Future

此前整理Java并发文章,在写任务终止的时候就提到过这个接口,我们来回顾一下:

 

[java] view plaincopyCallable、Future和CompletionService
            
    
    博客分类: Thread CallableFutureCompletionService Callable、Future和CompletionService
            
    
    博客分类: Thread CallableFutureCompletionService 
 
  1. public interface Future<V> {  
  2.     boolean cancel(boolean mayInterruptIfRunning);  
  3.     boolean isCancelled();  
  4.     boolean isDone();  
  5.     V get() throws InterruptedException, ExecutionException;  
  6.     V get(long timeout, TimeUnit unit)  
  7.         throws InterruptedException, ExecutionException, TimeoutException;  
  8. }  

 

接口中列出了5个方法,分两大方面。除了对任务终止处理以外,Future的作用就是通过get()方法拿到特定类型的结果。

2. FutureTask类与RunnableFuture接口

和Future接口一起,在java.util.concurrent包中,FutureTask是其具体的实现类。而在JDK1.6中,又引入了RunnableFuture这样一个概念:

 

[java] view plaincopyCallable、Future和CompletionService
            
    
    博客分类: Thread CallableFutureCompletionService Callable、Future和CompletionService
            
    
    博客分类: Thread CallableFutureCompletionService 
 
  1. public interface RunnableFuture extends Runnable, Future {  
  2.     void run();  
  3. }  

 

乍一看,其实并没有什么,不过是把Runnable和Future两个接口捏到一起了。实际上,这个接口用意应该是这样的,将需要run()的任务和结果结合在一起,执行了run()能够保证结果被设置从而可以获取到。

在ExecutorService中,submit()方法会有很多重载实现,有的用Runnable参数,有的用Callable参数。而对于submit()方法本身的实际操作,就是:

  • 执行任务
  • 返回Future对象

在实现中,AbstractExecutorService的submit()方法无论传入的是Callable还是Runnable,都会调用newTaskFor()将其转变为一个RunnableFuture对象,这个对象既会被用来调用Executor的execute()方法,也会作为Future结果返回。

JDK1.6之后,FutureTask也就成为了RunnableFuture的一个实现,当然也还是Future的实现类。我们再来简单看下它的实现。

前面文章提到过AbstractQueuedSynchronizer类(AQS)的应用,实际上FutureTask中也有一个名为Sync而且继承于AQS的内部类。在FutureTask的实现中,每个任务都有执行状态,其巧妙地运用了AQS提供的state属性和protected方法,保证了对Future结果获取线程的阻塞和唤醒。

3. CompleteService

最后稍微提一下CompleteService接口,这个接口是为了方便多个任务执行时,可以方便得获取到执行任务的Future结果。接口内容如下:

 

[java] view plaincopyCallable、Future和CompletionService
            
    
    博客分类: Thread CallableFutureCompletionService Callable、Future和CompletionService
            
    
    博客分类: Thread CallableFutureCompletionService 
 
  1. public interface CompletionService<V> {  
  2.     Future<V> submit(Callable<V> task);  
  3.     Future<V> submit(Runnable task, V result);  
  4.     Future<V> take() throws InterruptedException;  
  5.     Future<V> poll();  
  6.     Future<V> poll(long timeout, TimeUnit unit) throws InterruptedException;  
  7. }  

 

同样,也是五个方法,分为两大方面。一个是对Callable和Runnable类型参数的任务提交,另一方面则是尝试对结果以不同的方式进行获取,take()方法一般是阻塞式的获取,后两者则更灵活。

通常来讲,CompleteService是要和Executor结合在一起使用的,这在后面讲Executor的时候还会详细讲到。