Managing FutureTask and ExecutorService
The official way to support a system where you control multiple Future instances from an ExecutorService is similar to what @ferada has suggested, you use a 'decorator pattern' to wrap the task, and track the results. But, the Java library has this system built in to the ExecutorCompletionService. Using that service you are able to feed tasks to the service, and wait for tasks to complete.

Your code as it is looks functional, and you successfully wait for all tasks to complete. The problem is that you are not waiting for them in the right order (you are waiting in submit-order, not completion-order).

Since the CompletionService returns a Future on submit, and the exact same one on completion, you can use that as a key to a timing Map... using an IdentityHashMap on the Future would be appropriate because it removes any dependency on the actual implementation of Future.

Note that you can use an 'enhanced-for' loop to get the Caller instances from the list.

Consider the following code:

ExecutorService es = Executors.newCachedThreadPool();
ExecutorCompletionService&lt;Boolean&gt; ecs = new ExecutorCompletionService&lt;&gt;(es);
Map&lt;Future,Long&gt; times = new IdentityHashMap&lt;&gt;();
Map&lt;Future,Caller&gt; jobs = new IdentityHashMap&lt;&gt;();

for (Caller caller : callerList) {
    Future&lt;Boolean&gt; f = ecs.submit(caller);
    times.put(f, System.currentTimeMillis());
    jobs.put(f, caller);
}

while (!times.isEmpty()) {
    Future&lt;Boolean&gt; future = ecs.take();
    // note, remove the future from the map.
    long time = System.currentTimeMillis() - times.remove(future);
    System.out.println(jobs.get(future) + " result is " 
         + future.get() + " in " + time + "ms");
}


One important note here, is that I would not actually print the output of the result in the loop, but just record the details. The println is probably slow, and that would make the timing results biased. I would accumulate the completion data in a different way, and print it all after all tasks are done, or, alternatively, in a different thread.