views:

169

answers:

2

I want to convert this linear loop into a concurrent one:

for(Item item : ItemList) {
    processItem(item);
}

Is this really the shortest way to do this?

class Worker implements Runnable {
    Item item;
    Worker(Item item) {
        this.item = item;
    }
    public void Run() {
        processItem(item);
    }
}

ExecutorService exec = Executors.newFixedThreadPool(THREADPOOL_SIZE);
for(Item item : ItemList) {
    exec.execute(new Worker(item));
}
exec.shutdown();

boolean properFinish = false;
try {
    properFinish = exec.awaitTermination(50, TimeUnit.SECONDS);
} catch (InterruptedException e) { 
    Thread.currentThread().interrupt();
}

Specifically, I would like a way to use an anonymous class, but really, any way to make this shorter and more readable would be appreciated.

UPDATE: Just realized I was being a bit stupid, since it's pretty easy to use an anonymous class in this example:

for(final Item item : ItemList) {
    exec.execute(new Runnable() {
        public void run() {
            processItem(item);
        }
    });
}

In my original code, the loop was a simple for (i=0; i<=ItemList.length(); i++) and I couldn't think of a way to make i final in any way that makes sense. I guess using a "for each" loop make the situation better.

Still, any way to get rid of the rest of the boilerplate?

UPDATE 2: Using ExecutorCompletionService, assuming processItem returns a result.

ExecutorService exec = Executors.newFixedThreadPool(THREADPOOL_SIZE);
CompletionService<ResultType> ecs = new ExecutorCompletionService<ResultType>(executor);
for(final Item item : ItemList) {
    ecs.submit(new Callable<ResultType>() {
        public ResultType call() {
            return processItem(item);
        }
    });
}

for(Item item : item) {
    // Do whatever with the results
    ecs.take().get();
}

This does look nicer.

+6  A: 

Check out the ExecutorCompletionService in JDK 6 - rather then spinning your own. It allows you to execute a number of threads for an assigned task and get the results of each thread as it's processed.

Gandalf
ExecutorCompletionService is in JDK5, too.
Thilo
+1  A: 

If you do need the results, and want them in the same order as submitted then maybe do something like:

List<Future<ResultType>> futures = new ArrayList<Future<ResultType>>();
for(final Item item : items) {
  futures.add(executor.submit(...))
}
List<ResultType> results = new ArrayList<ResultType>();
for(Future<ResultType> future : futures) {
  results.add(future.get());
}
return results;

EDIT: If you're up to some over-engineering you can return an iterator instead doing the second loop (so that the caller could start processing results before all are available):

return Iterators.transform(results, new Function<Future<ResultType>,ResultType>() {
   public ResultType apply(Future<ResultType> future) {
       return future.get();
   }
})

using "Iterators" from the google collection library. Another possibility is to just return the list of Futures and let the caller do the waiting.