HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Managing FutureTask and ExecutorService

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
andfuturetaskexecutorservicemanaging

Problem

I have many unrelated tasks to execute and I have to get the result of the task so I use the FutureTask.

I submit all the task to the thread pool and use a map to keep track of the task and the FutureTask to get the result. And two for loops are used:

ExecutorService es = Executors.newCachedThreadPool();

Map> taskResultMap = new HashMap>();

for (int i = 0 ; i  futureTask = new FutureTask(caller);
    es.submit(futureTask);
    taskResultMap.put(caller, futureTask);

}

for (int i = 0 ; i  task = taskResultMap.get(caller);
    System.out.println(caller.getName() + " result is " + task.get());
}


Is this a good practice about ExecutorService and FutureTask? I'm new in multi-thread programming.

Another two questions:

  • How can I get the execution time for each thread? print in caller iteself or outside caller?



  • When I invoke the FutureTask.get() method in for loop, I think the get() method may be blocked, If a time-consuming task is running, I can't get the result of the next task but to wait. How could I do this?

Solution

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 ecs = new ExecutorCompletionService<>(es);
Map times = new IdentityHashMap<>();
Map jobs = new IdentityHashMap<>();

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

while (!times.isEmpty()) {
    Future 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.

Code Snippets

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

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

while (!times.isEmpty()) {
    Future<Boolean> 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");
}

Context

StackExchange Code Review Q#67304, answer score: 4

Revisions (0)

No revisions yet.