Metoda call to Future.get () blocks. Czy to naprawdę pożądane?

Proszę uważnie przeczytać pytanie przed oznaczeniem go jako DUPLIKAT.

Poniżej fragment pseudo kodu. Moje pytanie brzmi - czy poniższy kod nie pokonuje samego pojęcia równoległego przetwarzania asynchronicznego?

Powodem, dla którego o to pytam jest to, że w poniższym kodzie główny wątek przedstawi zadanie do wykonania w innym wątku. Po wysłaniu zadania w kolejce, blokuje on przyszłość.metoda get () dla zadania zwracająca wartość. Wolałabym zadanie wykonywane w głównym wątku, a nie przesyłanie do innego wątku i oczekiwanie na wyniki. Co zyskałem wykonując zadanie w nowym wątku?

Zdaję sobie sprawę, że można poczekać ograniczony czas itp., Ale co wtedy, jeśli naprawdę zależy mi na wyniku? Problem pogarsza się, jeśli istnieje wiele zadań do wykonania. Wydaje mi się, że po prostu wykonujemy pracę synchronicznie. Znam bibliotekę Guava, która zapewnia nie blokujący interfejs słuchacza. Ale jestem zainteresowany, Czy moje rozumienie jest poprawne na przyszłość.get () API. Jeśli to prawda, dlaczego jest przyszłość.get () zaprojektowany do blokowania w ten sposób pokonując cały proces przetwarzania równoległego?

Uwaga-dla przypomnienia używam Javy 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());
}
Author: The Monk, 2015-06-27

5 answers

Future oferuje metodę isDone(), która nie blokuje i zwraca true, jeśli obliczenia zostały zakończone, false w przeciwnym razie.

Future.get() służy do pobierania wyniku obliczeń.

Masz kilka opcji:

  • wywołaj isDone() i jeśli wynik jest gotowy poproś o to, wywołując get(), zauważ, że nie ma blokowania
  • blokuj w nieskończoność z get()
  • blok dla określonego limitu czasu z get(long timeout, TimeUnit unit)

Cała Future API rzecz jest tam aby mieć łatwy sposób uzyskiwania wartości z wątków wykonujących równoległe zadania. Można to zrobić synchronicznie lub asynchronicznie, jeśli wolisz, jak opisano w punktorach powyżej.

UPDATE WITH CACHE EXAMPLE

Oto implementacja pamięci podręcznej z współbieżności Javy w praktyce, doskonały przypadek użycia dla Future.

  • jeśli obliczenia są już uruchomione, rozmówca zainteresowany wynikiem obliczeń będzie czekał na zakończenie obliczeń
  • jeśli wynik jest gotowy w pamięci podręcznej, wywołujący go odbierze
  • jeśli wynik nie jest gotowy i obliczenia jeszcze się nie rozpoczęły, wywołujący rozpocznie obliczenia i zawiąże wynik w Future dla innych wywołujących.

To wszystko można łatwo osiągnąć za pomocą Future API.

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's 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());
        }
    }
  }
}
 35
Author: John,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-07 13:42:06

Poniżej fragment pseudo kodu. Moje pytanie brzmi - czy poniższy kod nie pokonuje samego pojęcia równoległego przetwarzania asynchronicznego?

Wszystko zależy od Twojego przypadku użycia:

  1. Jeśli naprawdę chcesz zablokować, aż uzyskasz wynik, użyj blocking get()
  2. Jeśli możesz poczekać na określony czas, aby poznać status, zamiast nieskończonego czasu blokowania, użyj get() Z time-out
  3. Jeśli możesz kontynuować bez analizy wyniku natychmiast i sprawdź wynik w przyszłości, użyj CompletableFuture (java 8)

    Przyszłość, która może być jawnie zakończona (ustawiając jej wartość i status) i może być używana jako CompletionStage, wspierająca zależne funkcje i akcje, które uruchamiają się po jej zakończeniu.

  4. Możesz zaimplementować mechanizm oddzwaniania z Runnable / Callable. Spójrz na pytanie poniżej:

    Java executors: jak być powiadamianym, bez blokowanie po zakończeniu zadania?

 6
Author: Ravindra babu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-23 15:13:41

W podanym przykładzie równie dobrze możesz uruchomić wszystko w swojej metodzie main() i pójść swoją wesołą drogą.

Ale załóżmy, że masz trzy kroki obliczeń, które aktualnie uruchamiasz sekwencyjnie. Dla zrozumienia Załóżmy, że Krok 1 zajmuje t1 sekund, Krok 2 zajmuje T2 sekund, a Krok 3 zajmuje T3 sekund, aby zakończyć. Zatem całkowity czas obliczeniowy wynosi t1+t2+t3. Załóżmy również, że t2>t1>=t3.

Zastanówmy się teraz nad scenariuszem, kiedy wykonaliśmy te trzy kroki równolegle za pomocą Future do przechowywania każdego wyniku obliczeniowego. Możesz sprawdzić, czy każde zadanie jest wykonane za pomocą nieblokującego isDone() wywołania na odpowiednich futures. Co teraz? teoretycznie twoja egzekucja jest tak szybka jak t2 Kończy się, prawda? Więc my osiągnęliśmy pewne korzyści z równoległości.

Ponadto, w Java8, istnieje CompletableFuture, która obsługuje wywołania w stylu funkcjonalnym.

 1
Author: ring bearer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-27 18:39:51

Chciałbym się podzielić tym, bardziej teoretycznym punktem widzenia, ponieważ są już pewne techniczne odpowiedzi. Chciałbym oprzeć swoją odpowiedź na komentarzu:

Pozwól, że dam ci mój przykład. Zadania, które składam do serwisu kończą się podnoszenie żądań HTTP, wynik żądania HTTP może zająć wiele czasu. Ale potrzebuję wyniku każdego żądania HTTP. Zadania są złożone w pętli. Jeśli czekam aż każde zadanie powróci (get), to tracę paralelizm tutaj, prawda?
Co zgadza się z tym, co zostało powiedziane w pytaniu. Powiedz, że masz trójkę dzieci i chcesz zrobić tort na urodziny. Ponieważ chcesz zrobić największe z ciast, potrzebujesz wiele różnych rzeczy, aby je przygotować. Więc to, co robisz, to dzielenie składników na trzy różne listy, ponieważ tam, gdzie mieszkasz, istnieją tylko 3 supermarkety, które sprzedają różne produkty i przypisują każdemu z Twoich dzieci jedno zadanie, simultaneously.

Teraz, przed możesz zacząć przygotowywać ciasto (Załóżmy jeszcze raz, że wcześniej potrzebujesz wszystkich składników) będziesz musiał poczekać na dziecko, które musi wykonać najdłuższą trasę. Teraz fakt, że musisz poczekać na wszystkie składniki przed rozpoczęciem robienia ciasta, jest Twoją koniecznością, a nie zależnością między zadaniami. Twoje dzieci pracują nad zadaniami tak długo, jak tylko mogły (np.: dopóki pierwsze dziecko nie ukończyło zadania). Podsumowując, tutaj masz paralelilsm.

Przykład sekwencyjny jest opisany, gdy masz 1 dziecko i przypisujesz mu wszystkie trzy zadania.

 1
Author: NiVeR,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-12 15:10:12

Jeśli nie zależy ci na wynikach, uruchom nowy wątek i z tego wątku użyj ExectorService API do wysyłania zadań. W ten sposób Twój wątek nadrzędny tj. main wątek nie będzie blokowany w żaden sposób, po prostu pojawi się nowy wątek, a następnie rozpocznie dalsze wykonywanie, podczas gdy nowy wątek wyśle Twoje zadania.

Do tworzenia nowego wątku-albo zrób to sam, mając ThreadFactory do tworzenia asynchronicznego wątku lub użyj jakiejś implementacji java.util.concurrent.Executor.

Jeśli jest to w Aplikacja JEE i używasz Spring framework wtedy możesz łatwo utworzyć nowy wątek asynchroniczny używając adnotacji @async.

Mam nadzieję, że to pomoże!
 0
Author: hagrawal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-27 19:00:05