2009-08-24 3 views
14

Мне нужно отправить несколько задач, а затем дождаться их, пока не будут доступны все результаты. Каждый из них добавляет String в Vector (который по умолчанию синхронизирован). Затем мне нужно начать новую задачу для каждого результата в Vector, но мне нужно сделать это, только когда все предыдущие задачи перестали выполнять свою работу.Исполнители Java: дождитесь завершения задачи.

Я хочу использовать Java-Executor, в частности, я попытался использовать Executors.newFixedThreadPool(100), чтобы использовать фиксированное число потоков (у меня есть переменная номер задачи, которая может быть 10 или 500), но я новичок с исполнителями, и я не знаю, как ждать завершения задачи. Это что-то вроде того, что псевдокод моя программа должна сделать:

ExecutorService e = Executors.newFixedThreadPool(100); 
while(true){ 

/*do something*/ 

for(...){ 
<start task> 
} 

<wait for all task termination> 

for each String in result{ 
<start task> 
} 

<wait for all task termination> 
} 

Я не могу сделать e.shutdown, потому что я в то время (правда), и мне нужно повторно использовать executorService .. .

Вы можете мне помочь? Можете ли вы предложить мне путеводитель/книгу о исполнителях java?

+0

Посмотрите на http://stackoverflow.com/questions/1228433/java-parallel-work-iterator/1228445 – Tim

ответ

21

ExecutorService дает вам механизм для одновременного выполнения нескольких задач и получения коллекции объектов Future назад (представляющих асинхронное вычисление задачи).

Collection<Callable<?>> tasks = new LinkedList<Callable<?>>(); 
//populate tasks 
for (Future<?> f : executorService.invokeAll(tasks)) { //invokeAll() blocks until ALL tasks submitted to executor complete 
    f.get(); 
} 

Если у вас есть Runnable с вместо Callable с, вы можете легко превратить Runnable в Callable<Object> с помощью метода:

Callable<?> c = Executors.callable(runnable); 
+4

Извините за то, что выкалываете старое сообщение, но я не вижу смысла в вызове f.get(); invokeAll() сам блокирует, поэтому нет необходимости вызывать get(), если вы не заинтересованы в результате (которого нет у вашего кода). – hooch

+0

@hooch: IMO вы всегда должны называть 'get()' в случае, если ваш 'Callable' выдал исключение. В противном случае он просто теряется. –

2

Когда вы отправляете службе исполнителя, вы получите объект Future.

Сохраните эти предметы в коллекции, а затем позвоните по телефону get() по очереди. get() до тех пор, пока основное задание не завершится, и поэтому результатом будет то, что вызов get() будет завершен после завершения всех основных заданий.

например.

Collection<Future> futures = ... 
for (Future f : futures) { 
    Object result = f.get(); 
    // maybe do something with the result. This could be a 
    // genericised Future<T> 
} 
System.out.println("Tasks completed"); 

Как только все это завершится, начните свое второе представление. Обратите внимание, что это может быть не оптимальное использование пула потоков, поскольку оно станет бездействующим, а затем вы его повторно заселяете. Если возможно, попробуйте и не забудьте заняться делами.

+0

В чем разница между e.submit (я думаю, мне нужно использовать это в соответствии с вашим примером) и e .Execute ?? – Raffo

+0

Разница заключается в том, что с отправкой вы получаете «Будущее назад» и с казнью вы этого не делаете. Если вы используете собственный 'ThreadFactory' с' UncaughtExceptionHandler', тогда 'execute' приведет к тому, что обработчик получит любые неперехваченные исключения, тогда как' submit' не будет - вы получите только исключения через '' '' '' '' '' '' '' 'метод –

14

Можете ли вы предложить мне GUIDE/книгу о Java исполнители ??

Я могу ответить на эту часть:

Java Concurrency in Practice Брайана Гетца (с Тимом Пайерлсом, Joshua Bloch, Джозеф Bowbeer, Дэвид Холмс и Doug Lea), скорее всего, ваш лучший выбор.

Это не только об исполнителях, хотя, но вместо этого охватывает java.util.concurrent пакет в целом, а также основные понятия и методы параллелизм, и некоторые более сложные темы, такие как модели памяти Java.

+0

Это отличная книга, хотя она и не для новичков. –

14

Вместо представления Runnable с или Callable сек к Executor непосредственно и хранения соответствующих Future возвращаемых значений я бы рекомендовал использовать CompletionService реализацию, чтобы получить каждый Future, когда он завершает. Этот подход отделяет производство задач от потребления завершенных задач, позволяя, например, создавать новые задачи в потоке производителя в течение определенного периода времени.

Collection<Callable<Result>> workItems = ... 
ExecutorService executor = Executors.newSingleThreadExecutor(); 
CompletionService<Result> compService = new ExecutorCompletionService<Result>(executor); 

// Add work items to Executor. 
for (Callable<Result> workItem : workItems) { 
    compService.submit(workItem); 
} 

// Consume results as they complete (this would typically occur on a different thread). 
for (int i=0; i<workItems.size(); ++i) { 
    Future<Result> fut = compService.take(); // Will block until a result is available. 
    Result result = fut.get(); // Extract result; this will not block. 
} 
+0

На практике это предполагает больше LOC, чем мой пример выше. CompletionService обычно полезен, если вы отправляете задания из разных мест, но хотите последовательно обрабатывать задачи (например, выполнять некоторые другие вычисления), которые вы хотите определить только один раз –

+1

@oxbow: или если вы хотите начать обработку результатов как только закончится первая задача! В противном случае вы могли бы ждать своей самой медленной задачи, в то время как другие уже были сделаны .. (Adamski +1) – Tim

+1

@Tim - OP сказал совершенно ясно, что он хотел дождаться завершения всех задач, поэтому это не имеет значения (кроме несколько наносекунд), задача которой заканчивается первой. –

1
ExecutorService executor = ... 
//submit tasks 
executor.shutdown(); // previously submitted tasks are executed, 
        // but no new tasks will be accepted 
while(!executor.awaitTermination(1, TimeUnit.SECONDS)) 
    ; 

Там нет простого способа сделать то, что вы хотите без создания пользовательских ExecutorService.