Summary

Structuring applications around the execution of tasks can simplify development and facilitate concurrency. The Executor framework permits you to decouple task submission from execution policy and supports a rich variety of execution policies; whenever you find yourself creating threads to perform tasks, consider using an Executor instead. To maximize the benefit of decomposing an application into tasks, you must identify sensible task boundaries. In some applications, the obvious task boundaries work well, whereas in others some analysis may be required to uncover finer-grained exploitable parallelism.

Listing 6.17. Requesting Travel Quotes Under a Time Budget.

private class QuoteTask implements Callable { private final TravelCompany company; private final TravelInfo travelInfo; ... public TravelQuote call() throws Exception { return company.solicitQuote(travelInfo); } } public List getRankedTravelQuotes( TravelInfo travelInfo, Set companies, Comparator ranking, long time, TimeUnit unit) throws InterruptedException { List tasks = new ArrayList(); for (TravelCompany company : companies) tasks.add(new QuoteTask(company, travelInfo)); List> futures = exec.invokeAll(tasks, time, unit); List quotes = new ArrayList(tasks.size()); Iterator taskIter = tasks.iterator(); for (Future f : futures) { QuoteTask task = taskIter.next(); try { quotes.add(f.get()); } catch (ExecutionException e) { quotes.add(task.getFailureQuote(e.getCause())); } catch (CancellationException e) { quotes.add(task.getTimeoutQuote(e)); } } Collections.sort(quotes, ranking); return quotes; }

Категории