Yes the shutdown() is required. shutdown() tells the threadpool that no more jobs can be submitted (it'll throw an exception if you try) and it means that the awaitTermination() will run until all the jobs finish (at what point the thread pool will shut down). The shutdown() is basically what awaitTermination() is waiting for.
It's a way of making the thing block until all work is done. There are other ways to do this (eg a CountdownLatch) but this way is pretty good.
As for whether you need to synchronize your List or not, well that depends on what you're doing with it.
Perhaps I should give you a framework for how to do this in a generic sense.
public interface Function<T> {
T execute(T input);
}
public static <T> List<T> map(List<T> input, final Function<T> func)
throws InterruptedException, ExecutionException {
List<Future<T>> futures = new ArrayList<Future<T>>(input.size());
ExecutorService executor = Executors.newFixedThreadPool(10);
for (final T item : input) {
futures.add(executor.submit(new Callable<T>() {
@Override
public T call() {
return func.execute(item);
}
}));
}
executor.shutdown();
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
List<T> ret = new ArrayList<T>(futures.size());
for (Future<T> future : futures) {
ret.add(future.get());
}
}
Not 100% sure this will work as is as I've just written it off the top of my head but the principles are sound. You can then do:
List<Integer> input = new ArrayList<Integer>();
for (int i=0; i<100; i++) {
input.add(i);
}
List<Integer> result = map(input, new Function<Integer>() {
@Override
public Integer execute(Integer in) {
return in * in;
}
});
It's about the best you can do in Java without closures or delegates.
How it works:
- It creates an intermediate list of Futures;
- Futures are the result of an operation. If the operation hasn't completed yet, calling Future.get() will block until it does return, otherwise it returns the result immediately;
- Each item of the list is passed in to the function passed in and mapped to a new value. This way the mapped operation doesn't need to be aware of any threading;
- The thread pool is shutdown. No more submissions are allowed;
- The awaitTermination() will block until the last thread finishes executing.
The above can be adapted to be synchronous too, which is useful because your functions are thread-agnostic.
Edit: as noted, technically you can get away without the awaitTermination(), instead simply doing a Future.get() since that will block but that method isn't as clean and may hide thread pool errors. Basically, shutdown/awaiTermination is just better. Alos if you get an exception on Future.get() you'll need to make sure you shutdown the thread pool in a finally block anyway.