Это хороший способ использовать 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. Вы уроните два Callables в 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 для вычисления, как только несколько других фьючерсы закончились.