2

I refer to this link to create a fixed size threadpool. Then I have a method which allow submit Callable request and get the result, it look like this:

 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();
    }
}

This execute method can be called many times and the request has difference execute time. The problem is that I want to get the result immediately when it finished. And I want to make sure when executing this method, other calls can be handled and allow add to thread poll.
Here is an example usage:

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;
            }
        });
    }

And the results are always in order although they have difference execute time:

  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

And I also used the future, but it doesn't work too.

 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();
    }
    }

Please tell me how can I do that? Thanks in advance.

Community
  • 1
  • 1
ductran
  • 10,043
  • 19
  • 82
  • 165
  • [This question is related,](http://stackoverflow.com/questions/826212/java-executors-how-to-be-notified-without-blocking-when-a-task-completes) but not exactly the same. – erickson Jan 11 '14 at 19:25

2 Answers2

3

You aren't using the CompletionService correctly. Your main thread is producing tasks and consuming results. A CompletionService is intended to decouple production and consumption; you'd use it when you have different threads playing these roles. The execute() method makes no sense; it is effectively doing this, but with a lot of obfuscation and overhead:

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

If you must consume the result as soon as it's ready, you have to make that part of the task. Otherwise, you need one application thread waiting for every task to complete, because if you don't, a task result might be ready and have to wait for a thread to be available to consume it. And if you have one thread per task already, why use a thread pool?

To be more explicit, if you want to guarantee no waiting, you need to do something like this:

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);
  }

}

If you want to use a CompletionService, you need some separate threads that take() from the service. But in this approach, if tasks are completed faster than they are consumed, some results will wait.

erickson
  • 265,237
  • 58
  • 395
  • 493
  • thanks, I need the ThreadPool because I want my tasks can run parallel in a pool with max size, and they should call back result when finish each task. Seems I just need to wrap my `Callable` task with a CallBack then it can work. – ductran Jan 12 '14 at 03:37
0

R4j,

the get() waits for the callable to return with the value from call: if you want to submit 5 requests you need to submit all requests and then call get

venergiac
  • 7,469
  • 2
  • 48
  • 70