Это хороший способ использовать java.утиль.параллельный.FutureTask?
прежде всего, я должен сказать, что я совершенно новичок в API java.утиль.возможно, то, что я делаю, совершенно неправильно.
что я хочу сделать?
у меня есть приложение Java, которое в основном запускает 2 отдельные обработки (называется myFirstProcess, mySecondProcess), но эти обработки должны выполняться одновременно.
Итак, я попытался сделать это:
public void startMyApplication() {
ExecutorService executor = Executors.newFixedThreadPool(2);
FutureTask<Object> futureOne = new FutureTask<Object>(myFirstProcess);
FutureTask<Object> futureTwo = new FutureTask<Object>(mySecondProcess);
executor.execute(futureOne);
executor.execute(futureTwo);
while (!(futureOne.isDone() && futureTwo.isDone())) {
try {
// I wait until both processes are finished.
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
logger.info("Processing finished");
executor.shutdown();
// Do some processing on results
...
}
myFirstProcess и mySecondProcess являются классами, которые реализуют Callable<Object>
, и где вся их обработка производится в методе call ().
вполне хорошо работает, но я не уверен, что это правильный способ сделать это. Хороший способ сделать то, что я хочу? Если нет, можете ли вы дать мне несколько советов по улучшению моего кода (и по-прежнему держать его как можно проще).
6 ответов
лучше использовать get()
метод.
futureOne.get();
futureTwo.get();
оба из которых ждут уведомления от потока, что он закончил обработку, это избавляет вас от занятого ожидания с таймером, который вы сейчас используете, что не эффективно и не элегантно.
в качестве бонуса, у вас есть API get(long timeout, TimeUnit unit)
что позволяет определить максимальное время для потока, чтобы спать и ждать ответа, и в противном случае продолжает работать.
посмотреть Java API дополнительные информация.
использование FutureTask
выше терпимы, но определенно не идиоматичны. Ты на самом деле заворачиваешь extra FutureTask
вокруг того, который вы представили ExecutorService
. Ваш FutureTask
трактуется как Runnable
на ExecutorService
. Внутренне, он обертывает ваш FutureTask
- as -Runnable
новая FutureTask
и возвращает его вам как Future<?>
.
вместо этого, вы должны представить свой Callable<Object>
экземпляры в CompletionService
. Вы уроните два Callable
s в via submit(Callable<V>)
, тогда повернись и позови CompletionService#take()
дважды (один раз для каждого представленного Callable
). Эти вызовы будут блокироваться до тех пор, пока одна, а затем и другие представленные задачи не будут завершены.
учитывая, что у вас уже есть Executor
в руке, построить новый ExecutorCompletionService
вокруг него и бросьте свои задачи там. Не вертитесь и не спите в ожидании;CompletionService#take()
блокируется до тех пор, пока одна из ваших задач не будет завершена (закончена или отменена) или поток, ожидающий take()
is прерванный.
решение Юваля в порядке. В качестве альтернативы вы также можете сделать это:
ExecutorService executor = Executors.newFixedThreadPool();
FutureTask<Object> futureOne = new FutureTask<Object>(myFirstProcess);
FutureTask<Object> futureTwo = new FutureTask<Object>(mySecondProcess);
executor.execute(futureOne);
executor.execute(futureTwo);
executor.shutdown();
try {
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
// interrupted
}
в чем преимущество такого подхода? На самом деле нет большой разницы, за исключением того, что таким образом вы останавливаете исполнителя, принимающего больше задач (вы можете сделать это и другим способом). Хотя я предпочитаю эту идиому той.
кроме того, если get() выдает исключение, вы можете оказаться в части вашего кода, которая предполагает, что обе задачи выполнены, что может быть плохо.
вы можете использовать invokeall (Colelction....) метод
package concurrent.threadPool;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class InvokeAll {
public static void main(String[] args) throws Exception {
ExecutorService service = Executors.newFixedThreadPool(5);
List<Future<java.lang.String>> futureList = service.invokeAll(Arrays.asList(new Task1<String>(),new Task2<String>()));
System.out.println(futureList.get(1).get());
System.out.println(futureList.get(0).get());
}
private static class Task1<String> implements Callable<String>{
@Override
public String call() throws Exception {
Thread.sleep(1000 * 10);
return (String) "1000 * 5";
}
}
private static class Task2<String> implements Callable<String>{
@Override
public String call() throws Exception {
Thread.sleep(1000 * 2);
int i=3;
if(i==3)
throw new RuntimeException("Its Wrong");
return (String) "1000 * 2";
}
}
}
вы можете использовать CyclicBarrier Если вы заинтересованы в запуске потоков одновременно или в ожидании их завершения, а затем выполните дальнейшую обработку. См. javadoc для получения дополнительной информации.
Если ваш futureTasks более 2, Прошу рассмотреть [ListenableFuture][1]
.
когда несколько операций должны начаться, как только другая операция начинается..."--5-->разветвителя " -- ListenableFuture просто работает: он запускает все запрошенные обратные вызовы. С немного больше работы, мы можем "вентилятор-в" или вызовите ListenableFuture для вычисления, как только несколько других фьючерсы закончились.