ExecutorService与CompletionService(ExecutorCompletionService)使用与探究

好文链接

  1. 深度剖析好文:“既生 ExecutorService, 何生 CompletionService?”
  2. ExecutorCompletionService的使用和实现

1.重大差异:

  • ExecutorService submit()的结果是Future, Future get() 方法的致命缺陷:如果 Future 结果没有完成,调用 get() 方法,程序会阻塞在那里,直至获取返回结果。
  • CompletionService 可以做到获取最先执行完的任务结果。

2.使用方式:

  • executorService.submit()后的结果需要保存在Future中,多个提交的结果保存在List<Future>中。
  • executorCompletionService.submit()的结果直接放在executorCompletionService中了,因为executorCompletionService的内部实现中有自己的队列。

使用示例

使用示例:见 “既生 ExecutorService, 何生 CompletionService?”,以下是复制过来的:
假设我们有 4 个任务(A, B, C, D)用来执行复杂的计算,每个任务的执行时间随着输入参数的不同而不同,如果将任务提交到 ExecutorService, 相信你已经可以“信手拈来”:

ExecutorService

ExecutorService executorService = Executors.newFixedThreadPool(4);
List<Future> futures = new ArrayList<Future<Integer>>();
futures.add(executorService.submit(A));
futures.add(executorService.submit(B));
futures.add(executorService.submit(C));
futures.add(executorService.submit(D));

// 遍历 Future list,通过 get() 方法获取每个 future 结果,这里的 get()会阻塞 
for (Future future:futures) {
    
    
    Integer result = future.get();
    // 其他业务逻辑
}

ExecutorCompletionService

ExecutorService executorService = Executors.newFixedThreadPool(4);

// ExecutorCompletionService 是 CompletionService 唯一实现类
CompletionService executorCompletionService= new ExecutorCompletionService<>(executorService );

List<Future> futures = new ArrayList<Future<Integer>>();
futures.add(executorCompletionService.submit(A));
futures.add(executorCompletionService.submit(B));
futures.add(executorCompletionService.submit(C));
futures.add(executorCompletionService.submit(D));

// 遍历 Future list,通过 get() 方法获取每个 future 结果,谁先有结果取谁
for (int i=0; i<futures.size(); i++) {
    
    
    Integer result = executorCompletionService.take().get();
    // 其他业务逻辑
}

那么,在executorCompletionService中,使用的List<Future>看起来并不需要,因为直接可以从 executorCompletionService.take().get();中获得结果。我们通过例子进一步验证:
关于这个例子的来源:ExecutorCompletionService的使用和实现

package threads.completeService;

import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

public class CallableTask implements Callable<Integer> {
    
    
  /**
   * 休眠时间
   */
  private int sleepSeconds;
  /**
   * 返回的值
   */
  private int returnValue;

  public CallableTask(int sleepSeconds, int returnValue) {
    
    
    this.sleepSeconds = sleepSeconds;
    this.returnValue = returnValue;
  }

  @Override
  public Integer call() throws Exception {
    
    
    System.out.println("begin to execute.");

    TimeUnit.SECONDS.sleep(sleepSeconds);

    System.out.println("end to execute.");

    return returnValue;
  }
  
}

public class Main {
    
    

  public static void main(String[] args) {
    
    
    int taskSize = 5;

    ExecutorService executor = Executors.newFixedThreadPool(taskSize);

    // 构建完成服务
    CompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(executor);
    int sleep = 5; // 睡眠时间,单位是秒,不是毫秒
    for (int i = 1; i <= taskSize; i++) {
    
    
      int value = i; // 返回结果
      // 向线程池提交任务
      completionService.submit(new CallableTask(sleep, value));//返回结果类型FutureTask
    }

    // 按照完成顺序,打印结果
    for (int i = 0; i < taskSize; i++) {
    
    
      try {
    
    
        System.out.println(completionService.take().get());// 阻塞,知道有任务完成可以获取结果
        // System.out.println(completionService.poll());//poll直接返回,不阻塞。但是没有完成的任务则返回null
        // System.out.println(completionService.poll(5, TimeUnit.SECONDS));//阻塞等待指定时间,如果有完成结果返回,没有的直接返回null																					// completionService.submit(new RunnableTask(),2);//completionService提交Runnable任务是无法获取结果的

      } catch (InterruptedException e) {
    
    
        e.printStackTrace();
      } catch (ExecutionException e) {
    
    
        e.printStackTrace();
      }
    }

    // 所有任务已经完成,关闭线程池
    System.out.println("执行完毕....");
    executor.shutdown();
  }
}

打印的结果是:

end to execute.
end to execute.
end to execute.
end to execute.
end to execute.
4
5
2
3
1

由于每个线程都sleep5s,谁先执行完具有随机性。我们将结果放在List<Future<Integer>>中,也是可以获取的:

  @Test
  public void test2() throws Exception{
    
    
    int taskSize = 5;

    ExecutorService executor = Executors.newFixedThreadPool(taskSize);

    // 构建完成服务
    CompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(executor);
    List<Future<Integer>> futures = new ArrayList<Future<Integer>>();
    int sleep = 5; // 睡眠时间,单位是秒,不是毫秒
    for (int i = 1; i <= taskSize; i++) {
    
    
      int value = i; // 返回结果
      // 向线程池提交任务
      futures.add(completionService.submit(new CallableTask(sleep, value)));//返回结果类型FutureTask
    }

    // 按照完成顺序,打印结果
    for (int i = 0; i < taskSize; i++) {
    
    
      try {
    
    
        System.out.println(completionService.take().get());// 阻塞,知道有任务完成可以获取结果
        // System.out.println(completionService.poll());//poll直接返回,不阻塞。但是没有完成的任务则返回null
        // System.out.println(completionService.poll(5, TimeUnit.SECONDS));//阻塞等待指定时间,如果有完成结果返回,没有的直接返回null																					// completionService.submit(new RunnableTask(),2);//completionService提交Runnable任务是无法获取结果的

      } catch (InterruptedException e) {
    
    
        e.printStackTrace();
      } catch (ExecutionException e) {
    
    
        e.printStackTrace();
      }
    }

    for (Future future:futures) {
    
    
      System.out.println("futures中取:"+future.get());
    }
    
    // 所有任务已经完成,关闭线程池
    System.out.println("执行完毕....");
    executor.shutdown();
  }

结果如下,从队列中取是按序的:

begin to execute.
begin to execute.
begin to execute.
begin to execute.
begin to execute.
end to execute.
end to execute.
end to execute.
end to execute.
3
4
2
1
end to execute.
5
futures中取:1
futures中取:2
futures中取:3
futures中取:4
futures中取:5
执行完毕....

示例结果表明:ExecutorCompletionService的结果可以直接从ExecutorCompletionService中取出,也可以放到List<Future>中,放到list中可以按序取出。

猜你喜欢

转载自blog.csdn.net/answer100answer/article/details/113119893