views:

646

answers:

4

I submitted 5 jobs to an ExecutorCompletionService, but it seems like the jobs are executed in sequence. The ExecutorService that is passed to the constructor of ExecutorCompletionService is created using newCacheThreadPool form. Am I doing anything wrong ?

UPDATE Each job is basically doing a database query & some calculation. The code for the ExecutorCompletionService is lifted as-is off the javadoc. I just replaced the Callables with my own custom Callable implementations.

A: 

The execution will depend on a number of things. For example:

  • the length of time it takes to complete a job
  • the number of threads in the thread pool (a cached thread pool will only create threads if it thinks they are needed)

Executing in sequence is not necessarily wrong.

Matthew Murdoch
It should be at least concerning in a multi-core environment.
kd304
If the jobs are short-lived then the pool may only ever need to create a single thread to execute them all. In this case only a single core will be used.
Matthew Murdoch
Yes, in case the job runs faster than you can submit task to the ES.
kd304
+1  A: 

I assume you meant Executors.newCachedThreadPool(). If so, execution should be parallelized as you expect.

Gili
+4  A: 

The ExecutorCompletionService has nothing to do with how jobs are executed, it's simply a convenient way of retrieving the results.

Executors.newCachedThreadPool by default executes tasks in separate threads, which can be parallel, given that:

  • tasks are independent, and don't e.g. synchronize on the same object inside;
  • you have multiple hardware CPU threads.

The last point deserves an explanation. Although there are no guarantees, in practice the Sun JVM favours the currently executing thread so it's never swapped out in favour of another one. That means that your 5 tasks might end up being executed serially due to the JVM implementation and not having e.g. a multi-core machine.

Robert Munteanu
The purpose of the cached tp is to start new threads if there are more work waiting in the work queue. Even in single core case the JVM (on Windows and Linux) will start new worker threads for the tasks.
kd304
+1  A: 

Each job is basically doing a database query & some calculation. The code for the ExecutorCompletionService is lifted as-is off the javadoc. I just replaced the Callables with my own custom Callable implementations.

In that case, are you sure you're not mistaken in thinking they're executed sequentially because you're retrieving the results sequentially?
Throw in some debug logging lines in your callables to rule this out, and/or have a look at this limited usage scenario:

public static void main(String... args) throws InterruptedException, ExecutionException {
 List<Callable<String>> list = new ArrayList<Callable<String>>();
 list.add(new PowersOfX(2));
 list.add(new PowersOfX(3));
 list.add(new PowersOfX(5));
 solve(Executors.newCachedThreadPool(), list);
}

static void solve(Executor e, Collection<Callable<String>> solvers) throws InterruptedException, ExecutionException {
 CompletionService<String> ecs = new ExecutorCompletionService<String>(e);
 for (Callable<String> s : solvers)
  ecs.submit(s);
 int n = solvers.size();
 for (int i = 0; i < n; ++i) {
  String r = ecs.take().get();
  if (r != null)
   System.out.println("Retrieved: " + r);
 }
}

static class PowersOfX implements Callable<String> {
 int x;
 public PowersOfX(int x) {this.x = x;}
 @Override
 public String call() throws Exception {
  StringBuilder sb = new StringBuilder();
  for (int i = 0; i < 10; i++) {
   sb.append(Math.pow(2, i)).append('\t');
   System.out.println(Math.pow(x, i));
   Thread.sleep(2000);
  }
  return sb.toString();
 }
}

Executing this you'll see the numbers are generated intermixed (and thus executed concurrently), but retrieving the results alone wont show you this level detail..

Tim