views:

193

answers:

1

I came up with a solution, but I wanted to see if anyone had a better way or more standard way of doing this.

The main method demonstrates how I would use this class. In a production version of this I would keep the thread pool open and continue to reuse it rather than shut it down after the calls.

public class MultiCallManager<T>
{

private ExecutorService service;
private Map<String, Future<T>> futureResults;
private Map<String, T> results;
private Map<String, Exception> errors;

public MultiCallManager(ExecutorService inService)
{
 service = inService;
 futureResults = new LinkedHashMap<String, Future<T>>();
 results = new LinkedHashMap<String, T>();
 errors = new LinkedHashMap<String, Exception>();
}

public void add(String key, Callable<T> task)
{
 Future<T> result = service.submit(task);
 futureResults.put(key, result);
}

public boolean hasErrors()
{
 return !errors.isEmpty();
}

public T getResult(String key)
{
 return results.get(key);
}

public Exception getException(String key)
{
 return errors.get(key);
}

public void waitUntilDone(long timeout)
{
 for (Entry<String, Future<T>> entry : futureResults.entrySet())
 {
  if (timeout < 0)
  {
   timeout = 0;
  }
  long start = System.currentTimeMillis();
  try
  {
   results.put(entry.getKey(), entry.getValue().get(timeout, TimeUnit.MILLISECONDS));
  }
  catch (Exception e)
  {
   errors.put(entry.getKey(), e);
  }
  long end = System.currentTimeMillis();
  long time = end - start;
  timeout = timeout - time;
 }
}

public static void main(String[] args) throws Exception
{
 ExecutorService service = Executors.newFixedThreadPool(5);

 MultiCallManager sandbox = new MultiCallManager(service);

 final String value = "AWESOME!";

 Callable<Object> c1 = new Callable<Object>()
 {

  @Override
  public String call() throws Exception
  {
   Thread.sleep(5000);
   return value;
  }
 };

 Callable<Object> c2 = new Callable<Object>()
 {

  @Override
  public Object call() throws Exception
  {
   Thread.sleep(6000);
   return value;
  }
 };

 Callable<Object> c3 = new Callable<Object>()
 {

  @Override
  public Object call() throws Exception
  {
   Thread.sleep(2000);
   return value;
  }
 };

 sandbox.add("c1", c1);
 sandbox.add("c2", c2);
 sandbox.add("c3", c3);
 sandbox.waitUntilDone(5000);

 if (sandbox.getResult("c1") != null)
 {
  System.out.println(sandbox.getResult("c1"));
 }
 else
 {
  sandbox.getException("c1").printStackTrace();
 }
 if (sandbox.getResult("c2") != null)
 {
  System.out.println(sandbox.getResult("c2"));
 }
 else
 {
  sandbox.getException("c2").printStackTrace();
 }
 if (sandbox.getResult("c3") != null)
 {
  System.out.println(sandbox.getResult("c3"));
 }
 else
 {
  sandbox.getException("c3").printStackTrace();
 }
 service.shutdownNow();
}

}

+3  A: 

Have a look at CompletionService and/or CountDownLatch they would accomplish some of what you are doing with a little nicer code.

CompletionService provides an interface similar to a BlockingQueue, so you can use the poll(long timeout, TimeUnit unit) method. Each time you poll, you'd want to set the timeout to the amount of time remaining for all threads, and just exit when time is up.

This essentially replaces your waitUntilDone method, performing the same job.

Mark Renouf
Actually waitUntilDone blocks when it calls entry.getValue().get(timeout, TimeUnit.MILLISECONDS), but I'll check out the stuff you posted. Thanks!
ScArcher2