Powód wywołania shutdown () w ExecutorService


84

Czytałam o tym trochę w ciągu ostatnich kilku godzin, a ja po prostu nie widzę żadnego powodu ( ważny powód), aby wywołać shutdown()na ExecutorService, chyba że mamy humongous aplikację, która przechowuje, dziesiątki i dziesiątki różnych usług executorów, które nie są wykorzystywane do długo.

Jedyną rzeczą (z tego, co wiem) powoduje zamknięcie, jest zrobienie tego, co robi normalny wątek po zakończeniu. Kiedy normalny wątek zakończy metodę uruchamiania Runnable (lub Callable), zostanie przekazany do Garbage Collection w celu zebrania. Dzięki usłudze Executor wątki zostaną po prostu wstrzymane, nie będą zaznaczane do czyszczenia pamięci. W tym celu konieczne jest wyłączenie.

Ok, wracając do mojego pytania. Czy jest jakiś powód, aby wywoływać wyłączenie ExecutorServicebardzo często, a nawet zaraz po przesłaniu mu niektórych zadań? Chciałbym zostawić przypadek, gdy ktoś to robi i zaraz po tym dzwoni, awaitTermination()ponieważ jest to sprawdzane. Kiedy już to zrobimy, musimy odtworzyć ExecutorServicewszystko od nowa, aby zrobić to samo. Czy to nie jest cały pomysł na ExecutorServiceponowne wykorzystanie wątków? Po co więc niszczyć ExecutorServicetak szybko?

Czy nie jest to racjonalny sposób, aby po prostu stworzyć ExecutorService(lub połączyć w pary w zależności od tego, ile potrzebujesz), a następnie podczas działania aplikacji przekazać im zadania, gdy się pojawią, a następnie przy zamknięciu aplikacji lub innych ważnych etapach zamknąć te executory ?

Chciałbym uzyskać odpowiedź od niektórych doświadczonych programistów, którzy piszą dużo kodu asynchronicznego za pomocą ExecutorServices.

Drugie pytanie poboczne, trochę mniejsze oferty dotyczące platformy Android. JEŚLI niektórzy z was powiedzą, że nie jest najlepszym pomysłem zamykanie wykonawców za każdym razem, a programujecie na Androidzie, czy moglibyście mi powiedzieć, jak radzicie sobie z tymi zamknięciami (a konkretnie - kiedy je wykonujemy), gdy mamy do czynienia z różnymi zdarzeniami cykl życia aplikacji.

Ze względu na komentarz CommonsWare post został napisany jako neutralny. Naprawdę nie jestem zainteresowany kłótniami na śmierć i wydaje się, że to tam prowadzi. Interesuje mnie tylko dowiedzenie się, o co pytałem tutaj doświadczonych programistów, jeśli zechcą podzielić się swoimi doświadczeniami. Dzięki.


3
„Widzę wiele razy przykładowe kody, w których przez cały czas występuje wywołanie shutdown () zaraz po przesłaniu lub wykonaniu zadań” - zachęcamy do korzystania z hiperłączy, aby udokumentować swoje roszczenia. Osobiście nigdy nie widziałem żadnych „przykładowych kodów”, które robią to, o czym mówisz. Możliwe, że coś błędnie interpretujesz i możemy ci to wskazać tylko wtedy, gdy wiemy, jakie „przykładowe kody” badasz.
CommonsWare

4
Cześć CommonsWare. Przede wszystkim widzę twój agresywny ton (a przynajmniej tak się wydaje) w stosunku do mnie, który, jak sądzę, nie jest tutaj potwierdzony. Nie próbowałem portretować ludzi w negatywny sposób. Jeśli chodzi o twój cytat, mówiłem głównie o edycji Thinking In Java IV, część Multitasking. Możesz znaleźć wiele przykładów tego w przykładach Bruce'a Eckela. Są w większości proste, ale mimo wszystko Bruce wywarł na mnie wrażenie, że bardzo często używam wyłączania. W każdym razie skupiłeś się na czymś, co nie było główną częścią mojego postu. Usunąłem te części, bo naprawdę nie chcę się o to kłócić.
Lucas

1
hay @CommonsWare in Thinking w książce java autorstwa Bruce'a Eckela .. na stronie Concurrency / Executor 804 Fourth Edition zawsze używa metody shutdown () zaraz po przesłaniu lub wykonaniu zadań w prostych aplikacjach, aby zilustrować, jak działa Executor tak, jak powiedział Lucas
Błąd

2
Wiem, że to stary post, ale myślę, że pytanie OP nadal jest aktualne i jest aktualne. Natknąłem się również na wiele przykładowych kodów, w których „istnieje wywołanie shutdown () zaraz po execute ()”. tutorials.jenkov.com/java-util-concurrent/executorservice.html (pierwszy samouczek, który pojawia się, gdy
wyszukujesz w

Dziękuję, miałem to samo pytanie, które zadałem przy pomocy tych „przykładowych kodów”. journaldev.com/2340/…
Gregordy

Odpowiedzi:


57

shutdown()Metoda robi jedno: uniemożliwia klientom wysłać więcej pracy z usługą executora. Oznacza to, że wszystkie istniejące zadania będą nadal działać do ukończenia, chyba że zostaną podjęte inne działania. Dzieje się tak nawet w przypadku zaplanowanych zadań, np. ScheduledExecutorService: nowe wystąpienia zaplanowanego zadania nie będą uruchamiane. Może to być przydatne w różnych scenariuszach.

Załóżmy, że masz aplikację konsolową, która ma usługę wykonawczą wykonującą N zadań. Jeśli użytkownik naciśnie CTRL-C, spodziewasz się, że aplikacja zakończy działanie, prawdopodobnie z wdziękiem. Co to znaczy wdzięcznie? Może chcesz, aby Twoja aplikacja nie była w stanie przesłać więcej zadań do usługi wykonawczej, a jednocześnie chcesz poczekać na zakończenie istniejących N zadań. Możesz to osiągnąć za pomocą haka zamykającego w ostateczności:

final ExecutorService service = ... // get it somewhere

Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Performing some shutdown cleanup...");
        service.shutdown();
        while (true) {
            try {
                System.out.println("Waiting for the service to terminate...");
                if (service.awaitTermination(5, TimeUnit.SECONDS)) {
                    break;
                }
            } catch (InterruptedException e) {
            }
        }
        System.out.println("Done cleaning");
    }
}));

Ten punkt zaczepienia wyłączy usługę, co uniemożliwi aplikacji przesyłanie nowych zadań i zaczeka na zakończenie wszystkich istniejących zadań przed zamknięciem maszyny JVM. Zakończenie oczekiwania zostanie zablokowane na 5 sekund i zwróci wartość true, jeśli usługa zostanie zamknięta. Odbywa się to w pętli, dzięki czemu masz pewność, że usługa zostanie ostatecznie zamknięta. Wyjątek InterruptedException zostaje za każdym razem połknięty. Jest to najlepszy sposób na zamknięcie usługi wykonawczej, która jest ponownie używana w całej aplikacji.

Ten kod nie jest doskonały. Jeśli nie jesteś absolutnie pewien, że Twoje zadania w końcu się zakończą, możesz poczekać na określony limit czasu, a następnie po prostu wyjść, porzucając uruchomione wątki. W takim przypadku sensowne byłoby również wywołanie shutdownNow()po przekroczeniu limitu czasu w ostatniej próbie przerwania uruchomionych wątków ( shutdownNow()poda również listę zadań oczekujących na uruchomienie). Jeśli twoje zadania są zaprojektowane tak, aby reagować na przerwy, to zadziała dobrze.

Innym interesującym scenariuszem jest sytuacja, gdy masz usługę ScheduledExecutorService, która wykonuje zadania okresowe. Jedynym sposobem na zatrzymanie łańcucha zadań okresowych jest zadzwonienie shutdown().

EDYCJA: Chciałbym dodać, że nie polecałbym używania haka zamykającego, jak pokazano powyżej w ogólnym przypadku: może to być podatne na błędy i powinno być tylko ostatecznością. Ponadto, jeśli masz zarejestrowanych wiele punktów zaczepienia zamykających, kolejność ich uruchamiania jest niezdefiniowana, co może być niepożądane. Wolałbym mieć aplikację jawnie wywołać shutdown()na InterruptedException.


Przepraszam Giovanni za spóźnioną odpowiedź i dziękuję za to przy okazji. Tak, wiem, jak działa Executor, co starałem się wyjaśnić w swoim pytaniu. Zamknięcie robi to, co powiedziałeś, a także umożliwia garbage collectorowi zebranie tych martwych wątków, aw efekcie zebranie instancji ExecutorService. Moje pytanie było konkretne. Czy jest jakiś powód, aby wywoływać funkcję „shutdown ()” przez cały czas, zaraz po przesłaniu / wykonaniu czegokolwiek na ExecutorService? Druga część pytania jest ściśle związana z architekturą Androida. Jeśli odpowiedź na poprzednie brzmi nie, to kiedy zadzwonić do wyłączenia podczas i. koło życia.
Lucas

3
Nie ma powodu, aby cały czas wywoływać funkcję shutdown (). W rzeczywistości może to być absolutnie niewłaściwa rzecz, ponieważ uniemożliwi to ponowne użycie usługi wykonawcy. Powodem wywołania tego na końcu cyklu życia usługi jest to, że wątki mogą zostać ostatecznie zebrane jako śmieci, jak zauważyłeś. Jeśli tego nie zrobisz, te wątki utrzymają maszynę JVM przy życiu, nawet jeśli są bezczynne.
Giovanni Botta

„Nie ma powodu, aby przez cały czas wywoływać shutdown (). W rzeczywistości może to być absolutnie niewłaściwa rzecz, ponieważ uniemożliwi to ponowne użycie usługi executora”. To jest dokładnie moje rozumowanie i mój dillema z pierwotnego pytania. Powtarzając, pytanie brzmi: kiedy powinienem zamknąć usługę ExecutiveService w cyklu życia Androida?
Lucas

2
Nie mam doświadczenia z Androidem, ale myślę, że powinieneś go zamknąć, gdy aplikacja zostanie zamknięta, aby umożliwić JVM ostateczne zamknięcie.
Giovanni Botta

3
Widzę. Sugeruję użycie buforowanej puli wątków i nigdy nie wywołuj shutdown()jej, aby nie marnować zasobów, gdy są niepotrzebne. Jeśli aplikacja zostanie zamknięta, wątki w puli zostaną ostatecznie usunięte z pamięci (domyślnie po 60 sekundach bezczynności). Zwróć uwagę, że jeśli chcesz, aby pula była ograniczona lub chcesz mieć inny czas życia wątku, możesz ThreadPoolExecutorbezpośrednio utworzyć plik .
Giovanni Botta

13

Czy nie jest cały pomysł, aby ExecutorService ponownie używał wątków? Dlaczego więc tak szybko niszczyć ExecutorService?

Tak. Nie powinieneś ExecutorServiceczęsto niszczyć i odtwarzać . Zainicjuj, ExecutorServicegdy potrzebujesz (głównie podczas uruchamiania) i utrzymuj ją aktywną, dopóki nie skończysz.

Czy nie jest to racjonalny sposób, aby po prostu utworzyć ExecutorService (lub parę w zależności od tego, ile potrzebujesz), a następnie podczas działania aplikacji przekazać im zadania, gdy się pojawią, a następnie po zamknięciu aplikacji lub innych ważnych etapach je wykonawcy?

Tak. Racjonalne jest zamykanie ExecutorServiceważnych etapów, takich jak zamykanie aplikacji itp.

Drugie pytanie poboczne, trochę mniejsze oferty dotyczące platformy Android. JEŚLI niektórzy z was powiedzą, że nie jest najlepszym pomysłem zamykanie wykonawców za każdym razem, a programujecie na Androidzie, czy moglibyście mi powiedzieć, jak radzicie sobie z tymi zamknięciami (a konkretnie, kiedy je wykonujemy), gdy mamy do czynienia z różnymi zdarzeniami w aplikacji koło życia.

Załóżmy, że ExecutorServicejest to współużytkowane przez różne działania w aplikacji. Każde działanie będzie wstrzymywane / wznawiane w różnych odstępach czasu, a nadal potrzebujesz jednego ExecutorServicedla swojej aplikacji.

Zamiast zarządzać stanem ExecutorServicemetod cyklu życia działania, przenieś zarządzanie ExecutorService (tworzenie / zamykanie) do usługi niestandardowej .

Utwórz ExecutorServicew Service => onCreate()i zamknij go poprawnie wonDestroy()

Zalecany sposób wyłączania ExecutorService:

Jak prawidłowo zamknąć java ExecutorService


3

Usługa ExecutorService powinna zostać zamknięta, gdy nie jest już potrzebna do zwolnienia zasobów systemowych i umożliwienia bezpiecznego zamykania aplikacji. Ponieważ wątki w ExecutorService mogą być wątkami nondaemon, mogą uniemożliwić normalne zakończenie działania aplikacji. Innymi słowy, aplikacja pozostaje uruchomiona po wykonaniu jej głównej metody.

Książka referencyjna

Opiekun: 14 Strona: 814


0

Powód wywołania shutdown () w ExecutorService

Dzisiaj spotkałem się z sytuacją, w której muszę poczekać, aż maszyna będzie gotowa, zanim rozpocznę serię zadań na tej maszynie.

Wykonuję wywołanie REST do tego komputera, jeśli nie otrzymam numeru 503 (serwer niedostępny), oznacza to, że maszyna jest gotowa do przetwarzania moich żądań. Tak więc czekam, aż otrzymam 200 (powodzenie) na pierwsze połączenie REST.

Istnieje wiele sposobów, aby to osiągnąć, użyłem ExecutorService do utworzenia wątku i zaplanowałem jego uruchamianie co X sekund. Więc muszę zatrzymać ten wątek pod warunkiem, sprawdź to ...

final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
    Runnable task = () -> {
        try {
            int statusCode = restHelper.firstRESTCall();

            if (statusCode == 200) {
                executor.shutdown();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    };

    int retryAfter = 60;
    executor.scheduleAtFixedRate(task, 0, retryAfter, TimeUnit.SECONDS);

Drugie pytanie poboczne, trochę mniejsze oferty dotyczące platformy Android.

Może mogę odpowiedzieć, jeśli podasz trochę więcej kontekstu! Z mojego doświadczenia w programowaniu na Androida wynika, że ​​rzadko potrzebujesz wątków. Tworzysz grę lub aplikację, która wymaga wątków do działania? Jeśli nie, w Androidzie masz inne sposoby rozwiązania problemów, takich jak scenariusz, który wyjaśniłem powyżej. Możesz raczej użyć TimerTask, AsyncTask lub Handlers lub Loaders w oparciu o kontekst. Dzieje się tak, ponieważ jeśli UIThread czeka długo, wiesz, co się stanie: /


0

Dzieje się tak niezależnie od planowanych przedsięwzięć, np. Dla ScheduledExecutorService: nowe przypadki zarezerwowanego zlecenia nie będą uruchamiane.

Powinniśmy oczekiwać, że masz wygodną aplikację, która ma administratora agenta wykonującego N zadań.

Nie łapię tego bez wysiłku? Być może potrzebujesz, aby Twoja aplikacja nie miała możliwości przesłania większej liczby zleceń do administracji agentów, a tymczasem musisz być cierpliwy, aby Twoje obecne zadania N zostały zakończone.

Z wyjątkiem, jeśli jesteś całkowicie przekonany, że Twoje sprawunki w końcu się wyczerpią, powinieneś siedzieć mocno na daną przerwę, a następnie po prostu wyjść, opuszczając biegnące struny.

W przypadku, gdy Twoje działania mają na celu reagowanie na zakłócenia, będzie to działać dobrze.

Inną intrygującą sytuacją jest punkt, w którym masz usługę ScheduledExecutorService, która odtwarza działanie.

Najlepszym sposobem na zatrzymanie łańcucha działań jest wywołanie funkcji shutdown ()

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.