在ThreadPool中完成任务后立即获得结果

时间:2014-01-11 18:18:52

标签: java threadpool

我引用this link来创建固定大小的线程池。然后我有一个允许提交Callable请求并获得结果的方法,它看起来像这样:

 private ExecutorService threadPool = Executors.newFixedThreadPool(5);
private CompletionService<String> pool = new ExecutorCompletionService<String>(threadPool);

 public void execute(Callable<String> request){
    pool.submit(request);
    // what happen if this method is called before get the result???
    try {
        String result = pool.take().get();
        System.out.println("result is " + result);
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
}

可以多次调用此execute方法,并且请求具有不同的执行时间。问题是我想在完成后立即获得结果。我想确保在执行此方法时,可以处理其他调用并允许添加线程轮询
以下是一个示例用法:

final Random rnd = new Random();
for (int i = 0; i < 5; i++) {
        final String value = String.valueOf(i);
        execute(new Callable<String>() {

            @Override
            public String call() throws Exception {
                int sleep = rnd.nextInt(10) * 100;
                System.out.println("sleep in " + sleep);
                Thread.sleep(sleep);
                return value;
            }
        });
    }

虽然执行时间不同,但结果总是按顺序排列:

  sleep in 900
  result is 0
  sleep in 300
  result is 1
  sleep in 0
  result is 2
  sleep in 500
  result is 3
  sleep in 600
  result is 4

我也使用future,但它也不起作用。

 private static void execute(Callable<String> request){
 Future<String> future = threadPool.submit(request);
    try {
        String result = future.get();
        System.out.println("result is " + result);
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
    }

请告诉我该怎么做?提前谢谢。

2 个答案:

答案 0 :(得分:2)

您没有正确使用CompletionService。您的主线程正在生成任务消耗结果。 CompletionService旨在解除生产和消费;当你有不同的线程播放这些角色时,你会使用它。 execute()方法毫无意义;它实际上是这样做的,但是有很多混淆和开销:

public void execute(Callable<String> request) {
  try {
    System.out.println("result is " + request.call());
  } catch (Exception ex) {
    ex.printStackTrace();
  }
}

如果必须在准备好后立即使用结果,则必须完成该任务的一部分。否则,您需要一个应用程序线程等待每个任务完成,因为如果不这样做,任务结果可能就绪,并且必须等待线程可用来使用它。如果每个任务已经有一个线程,为什么要使用线程池?

更明确地说,如果你想保证不等待,你需要做这样的事情:

final class MyTask implements Callable<Void> {

  private final String value;

  MyTask(String value) { this.value = value; }

  @Override
  public Void call() throws InterruptedException {
    String result = doWork();
    handleResult(result);
    return null;
  }

  private String doWork() throws InterruptedException {
    int sleep = ThreadLocalRandom.current().nextInt(10) * 100;
    System.out.println("sleep in " + sleep);
    Thread.sleep(sleep);
    return value;
  }

  private void handleResult(String result) {
    System.out.println("result is " + result);
  }

}

如果要使用CompletionService,则需要一些来自服务take()的单独线程。但是在这种方法中,如果任务的完成速度超过了它们的消耗速度,那么一些结果将会等待。

答案 1 :(得分:0)

R4j,

get()等待callable返回来自call的值:如果你想提交5个请求,你需要提交所有请求,然后调用get