Подтвердить что ты не робот

Вызов метода для блоков Future.get(). Это действительно желательно?

Пожалуйста, внимательно прочитайте вопрос, прежде чем отмечать это как дубликат.

Ниже приведен фрагмент псевдокода. Мой вопрос: не соответствует ли приведенный ниже код самому понятию параллельной асинхронной обработки?

Причина, по которой я спрашиваю об этом, заключается в том, что в приведенном ниже коде основной поток будет отправлять задачу в другой поток. После отправки задания в очередь он блокирует метод Future.get() для задачи, чтобы вернуть значение. Я предпочел бы, чтобы задача выполнялась в основном потоке, а не отправлялась в другой поток и ожидала результатов. Что я получил, выполнив задачу в новом потоке?

Я знаю, что вы можете подождать ограниченное время и т.д., но тогда что, если я действительно забочусь о результате? Проблема ухудшается, если выполняются несколько задач. Мне кажется, что мы просто делаем работу синхронно. Я знаю о библиотеке Guava, которая предоставляет неблокирующий интерфейс прослушивателя. Но мне интересно узнать, верно ли мое понимание для API Future.get(). Если это правильно, почему Future.get() предназначен для блокировки, тем самым побеждая весь процесс параллельной обработки?

Примечание. Для записи я использую JAVA 6

public static void main(String[] args){

private ExectorService executorService = ...

Future future = executorService.submit(new Callable(){
    public Object call() throws Exception {
        System.out.println("Asynchronous Callable");
        return "Callable Result";
    }
});

System.out.println("future.get() = " + future.get());
}
4b9b3361

Ответ 1

Future предлагает метод isDone(), который не блокирует и возвращает true, если вычисление завершено, в противном случае - false.

Future.get() используется для получения результата вычисления.

У вас есть несколько вариантов:

  • вызов isDone(), и если результат будет готов, попросите его, вызвав get(), обратите внимание, как нет блокировки
  • блокировать неопределенно с get()
  • для указанного таймаута с get(long timeout, TimeUnit unit)

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

ОБНОВЛЕНИЕ С ПРИМЕРОМ CACHE

Вот реализация кэша из Java Concurrency In Practice, отличный пример использования для Future.

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

Все это легко достигается с помощью API Future.

package net.jcip.examples;

import java.util.concurrent.*;
/**
 * Memoizer
 * <p/>
 * Final implementation of Memoizer
 *
 * @author Brian Goetz and Tim Peierls
 */
public class Memoizer <A, V> implements Computable<A, V> {
    private final ConcurrentMap<A, Future<V>> cache
            = new ConcurrentHashMap<A, Future<V>>();
    private final Computable<A, V> c;

public Memoizer(Computable<A, V> c) {
    this.c = c;
}

public V compute(final A arg) throws InterruptedException {
    while (true) {

        Future<V> f = cache.get(arg);
        // computation not started
        if (f == null) {
            Callable<V> eval = new Callable<V>() {
                public V call() throws InterruptedException {
                    return c.compute(arg);
                }
            };

            FutureTask<V> ft = new FutureTask<V>(eval);
            f = cache.putIfAbsent(arg, ft);
            // start computation if it not started in the meantime
            if (f == null) {
                f = ft;
                ft.run();
            }
        }

        // get result if ready, otherwise block and wait
        try {
            return f.get();
        } catch (CancellationException e) {
            cache.remove(arg, f);
        } catch (ExecutionException e) {
            throw LaunderThrowable.launderThrowable(e.getCause());
        }
    }
  }
}

Ответ 2

Ниже приведен фрагмент псевдокода. Мой вопрос is- Разве приведенный ниже код не опровергает само понятие параллельной асинхронной обработки?

Все зависит от вашего варианта использования:

  1. Если вы действительно хотите заблокировать, пока не получите результат, используйте блокировку get()
  2. Если вы можете ждать определенного периода, чтобы узнать статус, а не бесконечную продолжительность блокировки, используйте get() с тайм-аутом
  3. Если вы можете продолжить, не анализируя результат сразу и проверить результат в будущем, используйте CompletableFuture (Java 8)

    Будущее, которое может быть явно завершено (с указанием его значения и статуса) и может использоваться в качестве CompletionStage, поддерживающего зависимые функции и действия, которые запускаются после его завершения.

  4. Вы можете реализовать механизм обратного вызова из вашего Runnable/Callable. Посмотрите на вопрос SE ниже:

    Исполнители Java: как получить уведомление, не блокируя, когда задача завершена?

Ответ 3

В приведенном выше примере вы можете запустить все в своем методе main() и пойти своим весельем.

Но предположим, что у вас есть три шага вычисления, которые вы в настоящее время выполняете последовательно. Для понимания предположим, что step1 принимает t1 секунд, step2 принимает t2 секунды, а step3 занимает t3 секунды. Таким образом, общее время вычисления t1+t2+t3. Кроме того, предположим, что t2>t1>=t3.

Теперь рассмотрим сценарий, когда мы выполнили эти три шага параллельно, используя Future, чтобы провести каждый вычислительный результат. Вы можете проверить, выполняется ли каждая задача с помощью неблокирующего вызова isDone() на соответствующих фьючерсах. Теперь, что происходит? теоретически, ваше выполнение выполняется так же быстро, как t2 завершается правильно? Таким образом, мы получили некоторые преимущества от parallelism.

Кроме того, в Java8 существует CompletableFuture, который поддерживает обратные вызовы функционального стиля.

Ответ 4

Я хотел бы поделиться своим мнением по этому вопросу, более теоретически, поскольку уже есть некоторые технические ответы. Я хотел бы основать свой ответ на комментарии:

Позвольте мне привести вам мой пример. Задачи, которые я отправляю службе, в конечном итоге вызывают запросы HTTP. Результат запроса HTTP может занять много времени. Но мне нужен результат каждого HTTP-запроса. Задачи представлены в цикле. Если я жду, когда каждая задача вернется (получится), тогда я теряю здесь параллелизм, не так ли?

что согласуется с тем, что сказано в вопросе.

Скажем, у вас трое детей, и вы хотите сделать торт на свой день рождения. Так как вы хотите приготовить самый большой торт, вам понадобится много разных вещей для его приготовления. Итак, что вы делаете, это делите ингредиенты на три разных списка, потому что там, где вы живете, существует всего 3 супермаркета, которые продают разные продукты, и назначают каждому из ваших детей одну задачу simultaneously.

Теперь, прежде чем вы сможете начать готовить торт (допустим еще раз, что вам нужны все ингредиенты заранее), вам нужно будет дождаться ребенка, который должен пройти самый длинный маршрут. Теперь тот факт, что вам нужно подождать всех ингредиентов, прежде чем начинать готовить торт, - это ваша необходимость, а не зависимость между задачами. Ваши дети работали над заданиями одновременно так долго, как могли (например, пока первый ребенок не выполнил задание). Итак, в заключение, у вас есть паралеллизм.

Последовательный пример описан, когда у вас есть 1 ребенок, и вы назначаете ему все три задачи.

Ответ 5

Если вы не заботитесь о результатах, затем создайте новый поток и из этого потока используйте ExectorService API для представления задачи. Таким образом, поток родительского потока i.e main не будет блокировать каким-либо образом, он просто породит новый поток, а затем начнет дальнейшее выполнение, а новый поток отправит ваши задачи.

Для создания нового потока - сделайте это сами, создав ThreadFactory для создания потока асинхронных потоков или используйте некоторую реализацию java.util.concurrent.Executor.

Если это приложение JEE и вы используете среду Spring, вы можете легко создать новый асинхронный поток с помощью аннотации @async.

Надеюсь, это поможет!