`
IXHONG
  • 浏览: 451641 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

CompletionService、Future

阅读更多

一.CompletionService接口提供了可以操作异步任务的功能,其唯一实现的API为ExecutorCompletionService。此API只是可以获取异步任务执行的结果,它不是ExecutorService。

其有5个核心方法:

  • Future<V> poll():同步操作,获取并移除第一已经完成的任务,否则返回null。
  • Future<V> poll(timeout):同步操作,获取并移除第一个已经完成的任务,阻塞时间为timeout,否则返回null;支持InterruptedException。
  • Future<V> submit(Callable<V> task):提交任务,并获取任务执行结果的句柄。
  • Future<V> submit(Runnable,V result):提交任务,并获取任务执行结果的句柄。
  • Future<V> take():获取并移除第一个执行完成的任务,阻塞,直到有任务返回。支持InterruptedException。

 ExecutorCompletionService之说以能够提供此功能,原因就是其内部持有一个BockingQueue(此queue可以通过构造器传入指定)。

同时这还要借助Future/FutureTask的功能。

  • public ExecutorCompletionService(Executor executor,BlockingQueue<Future<V>> completionQueue):需要指定一个现有的executor和用于存储Future的队列,此后通过submit提交的任务都将有executor来执行,并将"Future句柄"添加到队列中;这个API很像一个"修饰者".

二.Future:提供了可以查看异步执行的结果。此接口提供了多个方便的方法,以便检测和控制任务的操作。

  • boolean cancel(boolean interruptIfRunning):试图取消任务的执行,如果任务已经完成或者取消,此操作将无效。如果任务尚未启动(start),那么任务将不会被执行,如果任务正在执行,则interruptIfRunning参数决定是否中断任务线程(线程需要相应“中断”)。此方法返回后,isDone将返回true;如果方法取消成功,则isCancelled()则返回true。
  • V get():等待并获取执行结果。此方法会阻塞,知道结果返回。此方法会在线程中断时抛出InterruptException,如果任务被取消,将;抛出异常。
  • V get(timeout):阻塞指定的时间。如果时间超时,仍未能执行完成,则抛出timeoutException。

RunnableFuture接口扩展了Future接口和Runnable,只提供(覆盖)run()方法,其作用非常简单,就是标示其子类具有可执行run方法,且获取Future结果。

三.FutureTask就是RunnableFuture的子类,具有Future接口的可取消任务的能力,以及获取异步计算结果的能力。FutureTask可以认为是一个runnable和callable任务的桥梁类,其构造函数可以接受这两种任务。

  • FutureTask(Callable<V> callable) 
  • FutureTask(Runnable runnable, V result):当运行结束后,将返回指定的result。

 此外,还有几个特殊的方法:

  • protected void done():可重写的方法,当任务执行结束后,将会调用此方法执行额外的操作。
  • protected void set(V v):会被run方法内部调用,用来设置执行结果,此结果可以通过get获取。

runnable类型的任务,会在FutureTask中转化成Callable(参见Executors.callable(runnable,result),原理很简单,创建一个Callable实例,即在调用call时间接的调用run(),

并在执行结束后,返回指定的result)。

 

四.ExecutorCompletionService:提交给ExecutorCompletionService的任务,会被封装成一个QueueingFuture(一个FutureTask子类),此类的唯一作用就是在done()方法中,增加了将执行的FutureTask加入了内部队列,此时外部调用者,就可以take到相应的执行结束的任务。(take就是从blockingQueue中依次获取)

 

 

Java代码  收藏代码
  1. public class ExcutorComplementServiceTest {  
  2.   
  3.     /** 
  4.  
  5.     * @param args 
  6.  
  7.     */  
  8.   
  9.     public static void main(String[] args) throws Exception{  
  10.   
  11.         Executor executor = Executors.newFixedThreadPool(3);  
  12.         CompletionService<Integer> cs = new ExecutorCompletionService<Integer>(executor);  
  13.         //List<Future<Integer>> result = new ArrayList<Future<Integer>>(10);  
  14.         for(int i=0; i< 10; i++){  
  15.             cs.submit(new Callable<Integer>() {          
  16.                 @Override  
  17.                 public Integer call() throws Exception {  
  18.                     Random r = new Random();  
  19.                     int init = 0;  
  20.                     for(int i = 0; i<100; i++){  
  21.                         init += r.nextInt();  
  22.                         Thread.sleep(100);  
  23.                     }  
  24.                     return Integer.valueOf(init);  
  25.                 }  
  26.             });  
  27.         }  
  28.         for(int i=0; i<10; i++){  
  29.             Future<Integer> future = cs.take();  
  30.             if(future != null){  
  31.                 System.out.println(future.get());  
  32.             }  
  33.         }   
  34.   
  35.     }  
  36.   
  37.    
  38.   
  39. }  

 

在前面的文章中我们讲述了创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口。

  这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果。

  如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦。

  而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。

  今天我们就来讨论一下Callable、Future和FutureTask三个类的使用方法。以下是本文的目录大纲:

  一.Callable与Runnable

  二.Future

  三.FutureTask

  四.使用示例

  若有不正之处请多多谅解,并欢迎批评指正。

  请尊重作者劳动成果,转载请标明原文链接:

  http://www.cnblogs.com/dolphin0520/p/3949310.html

  

一.Callable与Runnable

  先说一下java.lang.Runnable吧,它是一个接口,在它里面只声明了一个run()方法:

1
2
3
public interface Runnable {
    public abstract void run();
}

   由于run()方法返回值为void类型,所以在执行完任务之后无法返回任何结果。

  Callable位于java.util.concurrent包下,它也是一个接口,在它里面也只声明了一个方法,只不过这个方法叫做call():

1
2
3
4
5
6
7
8
9
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

   可以看到,这是一个泛型接口,call()函数返回的类型就是传递进来的V类型。

  那么怎么使用Callable呢?一般情况下是配合ExecutorService来使用的,在ExecutorService接口中声明了若干个submit方法的重载版本:

1
2
3
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);

  第一个submit方法里面的参数类型就是Callable。

  暂时只需要知道Callable一般是和ExecutorService配合来使用的,具体的使用方法讲在后面讲述。

  一般情况下我们使用第一个submit方法和第三个submit方法,第二个submit方法很少使用。

二.Future

  Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。

  Future类位于java.util.concurrent包下,它是一个接口:

1
2
3
4
5
6
7
8
public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

   在Future接口中声明了5个方法,下面依次解释每个方法的作用:

  • cancel方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。
  • isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。
  • isDone方法表示任务是否已经完成,若任务完成,则返回true;
  • get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
  • get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。

  也就是说Future提供了三种功能:

  1)判断任务是否完成;

  2)能够中断任务;

  3)能够获取任务执行结果。

  因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask。

三.FutureTask

  我们先来看一下FutureTask的实现:

1
public class FutureTask<V> implements RunnableFuture<V>

   FutureTask类实现了RunnableFuture接口,我们看一下RunnableFuture接口的实现:

1
2
3
public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}

   可以看出RunnableFuture继承了Runnable接口和Future接口,而FutureTask实现了RunnableFuture接口。所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。

  FutureTask提供了2个构造器:

1
2
3
4
public FutureTask(Callable<V> callable) {
}
public FutureTask(Runnable runnable, V result) {
}

  事实上,FutureTask是Future接口的一个唯一实现类。

四.使用示例

  1.使用Callable+Future获取执行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class Test {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool();
        Task task = new Task();
        Future<Integer> result = executor.submit(task);
        executor.shutdown();
         
        try {
            Thread.sleep(1000);
        catch (InterruptedException e1) {
            e1.printStackTrace();
        }
         
        System.out.println("主线程在执行任务");
         
        try {
            System.out.println("task运行结果"+result.get());
        catch (InterruptedException e) {
            e.printStackTrace();
        catch (ExecutionException e) {
            e.printStackTrace();
        }
         
        System.out.println("所有任务执行完毕");
    }
}
class Task implements Callable<Integer>{
    @Override
    public Integer call() throws Exception {
        System.out.println("子线程在进行计算");
        Thread.sleep(3000);
        int sum = 0;
        for(int i=0;i<100;i++)
            sum += i;
        return sum;
    }
}

   执行结果:

 View Code

  2.使用Callable+FutureTask获取执行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class Test {
    public static void main(String[] args) {
        //第一种方式
        ExecutorService executor = Executors.newCachedThreadPool();
        Task task = new Task();
        FutureTask<Integer> futureTask = new FutureTask<Integer>(task);
        executor.submit(futureTask);
        executor.shutdown();
         
        //第二种方式,注意这种方式和第一种方式效果是类似的,只不过一个使用的是ExecutorService,一个使用的是Thread
        /*Task task = new Task();
        FutureTask<Integer> futureTask = new FutureTask<Integer>(task);
        Thread thread = new Thread(futureTask);
        thread.start();*/
         
        try {
            Thread.sleep(1000);
        catch (InterruptedException e1) {
            e1.printStackTrace();
        }
         
        System.out.println("主线程在执行任务");
         
        try {
            System.out.println("task运行结果"+futureTask.get());
        catch (InterruptedException e) {
            e.printStackTrace();
        catch (ExecutionException e) {
            e.printStackTrace();
        }
         
        System.out.println("所有任务执行完毕");
    }
}
class Task implements Callable<Integer>{
    @Override
    public Integer call() throws Exception {
        System.out.println("子线程在进行计算");
        Thread.sleep(3000);
        int sum = 0;
        for(int i=0;i<100;i++)
            sum += i;
        return sum;
    }
}

   如果为了可取消性而使用 Future 但又不提供可用的结果,则可以声明 Future<?> 形式类型、并返回 null 作为底层任务的结果。

作者:海子
         
本博客中未标明转载的文章归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
分享到:
评论

相关推荐

    ExecutorService与CompletionService对比详解.docx

    提交任务的方式与ExecutorService类似,但获取结果时,不再直接从列表中获取Future,而是通过调用CompletionService的take()方法。take()方法会阻塞,直到有任务完成并将其结果放入队列,然后返回这个结果。这样,...

    Java利用future及时获取多线程运行结果

    使用CompletionService可以将Future对象添加到阻塞队列中,然后使用take()方法来获取Future对象的结果,这样可以实时获取多线程运行结果。 使用Future和CompletionService可以实现异步计算和非阻塞的任务调用,并...

    浅谈Java多线程处理中Future的妙用(附源码)

    CompletionService是一个可以帮助我们并发处理任务的工具,它可以将Future收集到一个队列里,可以按结果处理完成的先后顺序进队。这样我们可以轻松地实现并发处理大量任务,并且可以方便地获取处理结果。 下面是一...

    32 请按到场顺序发言—Completion Service详解.pdf

    与直接使用`ExecutorService`不同的是,我们不是通过`Future.get()`方法阻塞等待结果,而是使用`CompletionService`的`take()`方法来获取已完成任务的第一个结果,这将返回一个`Future`对象,从中我们可以获取任务的...

    通过多线程任务处理大批量耗时业务并返回结果

    `CompletionService`通过`Future`接口返回每个任务的结果,使得我们可以在任务完成的瞬间得到结果,而无需等待所有任务执行完毕。这对于处理大批量任务并需要实时处理结果的场景非常有用。 在提供的`ThreadDemo`...

    java.util.concurrent.uml.pdf

    CompletionService是一个接口,用于管理异步任务的完成情况。ExecutorCompletionService类是其实现,它利用线程池执行任务,并帮助开发者获取已经完成的任务结果。 Runnable和Callable是两种任务类型。Runnable是...

    java并发(二十四)多线程结果组装

    `ExecutorService`是一个线程池服务,它可以管理和控制多个线程的执行,而`Future`代表了异步计算的结果,可以检查计算是否完成、获取结果或取消任务。`Future`接口提供了`get()`方法来获取结果,该方法会阻塞直到...

    java多线程并发编程例子

    `TestCompletionService.java`可能展示了如何结合`ExecutorService`和`Future`来使用`CompletionService`。 4. **ScheduledThreadPoolExecutor**:这个类扩展了`ThreadPoolExecutor`,增加了定时和周期性执行任务的...

    java多线程异步性

    在Java中,通常通过Future和Callable接口实现异步计算,或者使用ExecutorService和CompletionService来管理和控制异步任务。 三、ExecutorService与ThreadPoolExecutor ExecutorService是Java并发框架中的核心接口...

    springboot集成amazon aws s3对象存储sdk(javav2)

    Future&lt;PartETag&gt; future = completionService.take(); partETags.add(future.get()); } CompleteMultipartUploadRequest completeRequest = CompleteMultipartUploadRequest.builder() .bucket(bucketName) ...

    Executor框架使用详解

    `CompletionService`接口扩展了`ExecutorService`,它提供了获取已完成任务的便捷方式,特别适用于处理大量并发任务时,需要按完成顺序处理结果的场景。 `Callable`接口类似于`Runnable`,但它可以返回一个结果。`...

    JAVA并发编程实践-线程执行-学习笔记

    CompletionService是一个高级的并发工具,它结合了Executor和BlockingQueue的功能,允许处理已完成的任务,而无需等待所有任务完成。这对于处理大量并发任务并需要及时处理已完成任务的场景非常有用。 为任务设置...

    JAVA高质量并发详解,多线程并发深入讲解

    - **Future模式:** 异步获取结果的模式,通过Future对象可以在任何时候查询结果,即使结果尚未计算完成。 - **Fork/Join框架:** 一种基于工作窃取(Work Stealing)算法的并发框架,能够有效地利用多核处理器的...

    Java并发编程实战

    6.3.5 CompletionService:Executor与BlockingQueue 6.3.6 示例:使用CompletionService实现页面渲染器 6.3.7 为任务设置时限 6.3.8 示例:旅行预定门户网站 第7章 取消与关闭 第8章 线程池的使用 第9章 图形...

    Java 并发编程实战

    6.3.5 CompletionService:Executor与BlockingQueue 6.3.6 示例:使用CompletionService实现页面渲染器 6.3.7 为任务设置时限 6.3.8 示例:旅行预定门户网站 第7章 取消与关闭 第8章 线程池的使用 第9章 图形...

    个人总结的深入java多线程开发

    9)已完成任务队列CompletionService 36 10)计时器CountDownLatch 37 11)周期性同步工具CyclicBarrier 38 12)异步计算的结果Future 40 13)安排线程池ScheduledExecutorService 40 五多线程面试题 41 六其他主题 41 1)...

    02-4(马士兵)-多线程并发

    5. Future和Callable接口:Callable接口允许创建返回值的任务,而Future接口代表Callable任务的结果,并提供检查任务是否完成、获取结果、取消任务等方法。 6. Atomic类:提供了一组原子操作的类,如AtomicInteger...

    JAVA课程学习笔记.doc

    - `java.util.concurrent.CompletionService`:允许获取执行任务的结果,`ExecutorCompletionService` 是其具体实现,结合了 `ExecutorService` 和 `Future`。 5. 线程池执行原理 线程池的执行过程主要包括任务提交...

    concurrent-all-in-one.pdf

    17. **CompletionService** - 提供一种方式,一旦任务完成,就获取并处理结果。 18. **线程服务的优雅停止** - 使用`shutdown()`和平稳关闭线程池,等待已提交任务完成。 - `shutdownNow()`尝试停止所有任务,...

Global site tag (gtag.js) - Google Analytics