Obsługa przerwanego wystąpienia w Javie

Jaka jest różnica między następującymi sposobami obsługi InterruptedException? Jak najlepiej to zrobić?

try{
 //...
} catch(InterruptedException e) { 
   Thread.currentThread().interrupt(); 
}

Lub

try{
 //...
} catch(InterruptedException e) {
   throw new RuntimeException(e);
}

EDIT: chciałbym również wiedzieć, w jakich scenariuszach są one używane.

Author: Nathan Hughes, 2010-10-20

7 answers

Prawdopodobnie przyszedłeś zadać to pytanie, ponieważ nazwałeś metodę, która rzuca InterruptedException.

Po pierwsze, powinieneś zobaczyć throws InterruptedException Co to jest: część podpisu metody i możliwy wynik wywołania metody, którą wywołujesz. Zacznij więc od przyjęcia faktu, że InterruptedException jest całkowicie poprawnym wynikiem wywołania metody.

Teraz, jeśli metoda, którą wywołujesz rzuca taki wyjątek, co powinna zrobić twoja metoda? Możesz znaleźć odpowiedź przez myślenie o:

Czy to ma sens dla metody , którą wdrażasz , aby rzucić InterruptedException? mówiąc inaczej, czy InterruptedException jest sensownym wynikiem wywołaniaTwojej metody ?

  • Jeśli tak , to throws InterruptedException powinna być częścią Twojej metody i powinieneś pozwolić, aby wyjątek rozprzestrzeniał się (tzn. nie łap go w ogóle).

    Przykład: Twoja metoda czeka na wartość z sieci, aby zakończyć obliczenia i zwrócić wynik. Jeśli blokujące połączenie sieciowe rzuca InterruptedException, twoja metoda nie może zakończyć obliczeń w normalny sposób. Pozwalasz InterruptedException rozmnażać się.

    int computeSum(Server server) throws InterruptedException {
        // Any InterruptedException thrown below is propagated
        int a = server.getValueA();
        int b = server.getValueB();
        return a + b;
    }
    
  • If no , then you should not declare your method with throws InterruptedException and you should (must!) Złap wyjątek. Teraz ważne są dwie rzeczy, o których należy pamiętać w tej sytuacji:]}

    1. Ktoś przerwał Ci wątek. Że ktoś jest pewnie chętny do anuluj operację, Zakończ program z wdziękiem, czy cokolwiek. Powinieneś być uprzejmy dla tego kogoś i wrócić z metody bez dalszych ceregieli.

    2. Nawet jeśli twoja metoda potrafi wygenerować sensowną wartość zwracaną w przypadku InterruptedException, fakt, że wątek został przerwany, może nadal mieć znaczenie. W szczególności kod wywołujący metodę może być zainteresowany tym, czy podczas wykonywania metody doszło do przerwania. Ty należy zatem odnotować fakt przerwania przez ustawienie flagi przerwanej: Thread.currentThread().interrupt()

    Przykład: użytkownik poprosił o wydrukowanie sumy dwóch wartości. Drukowanie "Failed to compute sum " jest dopuszczalne, jeśli suma nie może być obliczona (i znacznie lepiej niż pozwolić programowi zawiesić się ze śladem stosu z powodu InterruptedException). Innymi słowy, nie ma sensu deklarowanie tej metody za pomocą throws InterruptedException.

    void printSum(Server server) {
         try {
             int sum = computeSum(server);
             System.out.println("Sum: " + sum);
         } catch (InterruptedException e) {
             Thread.currentThread().interrupt();  // set interrupt flag
             System.out.println("Failed to compute sum");
         }
    }
    

Do tej pory powinno być jasne, że samo robienie throw new RuntimeException(e) to zły pomysł. To nie jest zbyt uprzejme dla rozmówcy. Możesz wymyślić nowy wyjątek runtime, ale główna przyczyna (ktoś chce, aby wątek przestał działać) może zostać utracona.

Inne przykłady:

Realizacja Runnable: Jak być może odkryłeś, podpis Runnable.run nie pozwala na ponowne rozważenie InterruptedExceptions. Cóż, ty zapisałeś się na implementację Runnable, co oznacza, że ty zapisałeś się na możliwe InterruptedExceptions. Albo wybrać inny interfejs, taki jak Callable, lub postępuj zgodnie z drugim podejściem powyżej.

 

Calling Thread.sleep: próbujesz odczytać plik, a specyfikacja mówi, że powinieneś spróbować 10 razy z 1 sekundą pomiędzy. Dzwonisz Thread.sleep(1000). Więc musisz sobie poradzić z InterruptedException. W przypadku metody takiej jak tryToReadFile sensowne jest powiedzenie, "jeśli ktoś mi przerywa, nie mogę dokończyć mojej czynności, próbując przeczytać plik " . Innymi słowy, to ma sens dla metody rzucania InterruptedExceptions.

String tryToReadFile(File f) throws InterruptedException {
    for (int i = 0; i < 10; i++) {
        if (f.exists())
            return readFile(f);
        Thread.sleep(1000);
    }
    return null;
}

Ten post został przepisany jako artykuł tutaj .

 298
Author: aioobe,
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-11-28 12:23:39

Tak się składa, że czytałem o tym dziś rano w drodze do pracy wwspółbieżność Javy w praktyce Briana Goetza. Zasadniczo mówi, że powinieneś zrobić jedną z dwóch rzeczy

  1. Propagować InterruptedException - Zadeklaruj swoją metodę rzucania sprawdzonego InterruptedException, aby twój rozmówca musiał sobie z tym poradzić.

  2. Przywróć przerwanie - czasami nie można rzucić InterruptedException. W takich przypadkach należy złapać InterruptedException i przywrócić przerwanie status wywołując metodę interrupt() na currentThread, aby Kod wyżej na stosie połączeń mógł zobaczyć, że zostało wydane przerwanie.

 73
Author: mR_fr0g,
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
2014-02-24 13:04:54

Co próbujesz zrobić?

InterruptedException jest wyrzucany, gdy wątek czeka lub śpi, a inny wątek przerywa go za pomocą metody interrupt w klasie Thread. Jeśli więc złapiesz ten wyjątek, oznacza to, że wątek został przerwany. Zwykle nie ma sensu ponownie wywoływać Thread.currentThread().interrupt();, chyba że chcesz sprawdzić status "przerwany" wątku z innego miejsca.

Jeśli chodzi o inną opcję rzucania RuntimeException, nie wydaje się to zbyt mądre (kto będzie łapiesz? jak to będzie obsługiwane?), ale trudno powiedzieć więcej bez dodatkowych informacji.

 16
Author: Grodriguez,
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
2010-10-20 09:30:24

Dla mnie najważniejsze w tym jest to, że InterruptedException to nie wszystko, co idzie źle, to wątek robi to, co mu kazałeś. W związku z tym rethrowing to owinięte w RuntimeException nie ma sensu.

W wielu przypadkach ma to sens, aby rethrow wyjątek zawinięty w RuntimeException, kiedy mówisz, Nie wiem, co poszło nie tak i nie mogę zrobić nic, aby to naprawić, chcę tylko, aby wydostać się z bieżącego przepływu przetwarzania i trafić jakikolwiek wyjątek dla całej aplikacji handler, którego mam, żeby mógł to zarejestrować. Tak nie jest w przypadku InterruptedException, to po prostu wątek reagujący na wywołanie interrupt (), rzuca InterruptedException, aby pomóc anulować przetwarzanie wątku w odpowiednim czasie.

Więc rozpropaguj InterruptedException, lub zjedz je inteligentnie (czyli w miejscu, w którym osiągnie to, co miało być zrobione) i zresetuj flagę przerwania. Zauważ, że flaga przerwania zostanie wyczyszczona, gdy InterruptedException jest odrzucane; założenie, które twórcy biblioteki Jdk zakładają, że przechwycenie wyjątku sprowadza się do obsługi go, więc domyślnie flaga jest czyszczona.

Więc zdecydowanie pierwszy sposób jest lepszy, drugi zamieszczony przykład w pytaniu nie jest przydatny, chyba że nie oczekujesz, że wątek rzeczywiście zostanie przerwany, a przerwanie go oznacza błąd.

Oto odpowiedź, którą napisałem opisując działanie przerwań, na przykładzie . Można zobaczyć w przykładowy kod, w którym używa przerywanego odstępu, aby wydostać się z pętli while W metodzie runnable.

 11
Author: Nathan Hughes,
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
2017-05-23 11:54:40

Prawidłowym domyślnym wyborem jest dodaj przerywaną wyjątek do listy rzutów. Przerwanie wskazuje, że inny wątek życzy sobie zakończenia wątku. Powód tego żądania nie jest oczywisty i jest całkowicie kontekstowy, więc jeśli nie masz dodatkowej wiedzy, powinieneś założyć, że jest to tylko przyjazne zamknięcie, a wszystko, co pozwala uniknąć zamknięcia, jest nie przyjazną odpowiedzią.

Java nie będzie losowo rzucać przerywane, wszystkie porady nie będą miały wpływu na twoje aplikacji, ale mam natknąć się na przypadek, w którym deweloper po strategii "jaskółka" stał się bardzo niewygodne. Zespół opracował duży zestaw testów i użył wątku.Śpij dużo. Teraz zaczęliśmy uruchamiać testy na naszym serwerze CI i czasami z powodu wad w kodzie utknęliśmy w stałych oczekiwaniach. Aby pogorszyć sytuację, przy próbie anulowania zadania CI nigdy się nie zamykał, ponieważ wątek.Przerwanie, które miało na celu przerwanie testu, nie przerwało zadania. Mieliśmy aby zalogować się do pola i ręcznie zabić procesy.

Krótko mówiąc, jeśli po prostu rzucisz InterruptedException, pasujesz do domyślnej intencji, że Twój wątek powinien się zakończyć. Jeśli nie możesz dodać InterruptedException do listy rzutów, zawiń go w RuntimeException.

Istnieje bardzo racjonalny argument, że InterruptedException powinno być samą RuntimeException, ponieważ zachęcałoby to do lepszej" domyślnej " obsługi. It ' s not a RuntimeException tylko dlatego, że projektanci trzymali się kategorycznej zasady, że RuntimeException powinien reprezentować błąd w kodzie. Ponieważ przerwane przekroczenie nie wynika bezpośrednio z błędu w kodzie, nie jest. Ale rzeczywistość jest taka, że często pojawia się InterruptedException, ponieważ w Twoim kodzie występuje błąd (np. nieskończona pętla, dead-lock), a przerwanie jest metodą innego wątku do radzenia sobie z tym błędem.

Jeśli wiesz, że jest racjonalne sprzątanie do zrobienia, to zrób to. Jeśli znasz głębszą przyczynę przerwania, możesz zająć się bardziej kompleksową obsługą.

Więc w podsumowaniu twoje wybory do obsługi powinny podążać za tą listą:

  1. domyślnie Dodaj do rzutów.
  2. Jeśli nie można dodawać do rzutów, należy rzucić RuntimeException (e). (najlepszy wybór wielu złych opcji)
  3. tylko wtedy, gdy znasz wyraźną przyczynę przerwania, postępuj zgodnie z życzeniem. Jeśli obsługa jest lokalna dla metody, reset przerwany przez połączenie do nitki.currentThread ().interrupt ().
 6
Author: nedruod,
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
2013-09-20 21:32:50

Chciałem tylko dodać ostatnią opcję do tego, o czym większość ludzi i artykułów wspomina. Jak stwierdził mR_fr0g, ważne jest, aby poprawnie obsłużyć przerwanie poprzez:

  • Propagacja InterruptException

  • Przywróć Stan przerwania w wątku

Lub dodatkowo:

  • Niestandardowa obsługa przerwań

Nie ma nic złego w obsłudze przerwania w niestandardowy sposób w zależności od okoliczności. Jako przerywnik jest żądaniem wypowiedzenia, w przeciwieństwie do wymuszonego polecenia, jest całkowicie poprawne, aby wykonać dodatkową pracę, aby aplikacja mogła obsłużyć żądanie z gracją. Na przykład, jeśli wątek jest uśpiony, czeka na IO lub odpowiedź sprzętową, po otrzymaniu przerwania, to jest całkowicie poprawne, aby z wdziękiem zamknąć wszelkie połączenia przed zakończeniem wątku.

Gorąco polecam zrozumienie tematu, ale ten artykuł jest dobrym źródłem informacji: http://www.ibm.com/developerworks/java/library/j-jtp05236/

 1
Author: TheIT,
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
2013-12-17 02:38:10

Powiedziałbym, że w niektórych przypadkach nic nie można robić. Prawdopodobnie nie jest to coś, co powinieneś robić domyślnie, ale w przypadku, gdy nie powinno być sposobu na przerwanie, nie jestem pewien ,co jeszcze zrobić(prawdopodobnie błąd logowania, ale to nie wpływa na przepływ programu).

Jeden przypadek dotyczy sytuacji, gdy masz zadanie (blokowanie) kolejki. Jeśli masz wątek daemona obsługujący te zadania i nie przerywasz wątku samodzielnie (z mojej wiedzy jvm nie przerywa daemona wątki na JVM shutdown), nie widzę sposobu na przerwanie, dlatego można je po prostu zignorować. (Wiem, że wątek daemona może zostać zabity przez jvm w dowolnym momencie i dlatego w niektórych przypadkach nie nadaje się).

Edytuj: Innym przypadkiem mogą być bloki strzeżone, przynajmniej w oparciu o samouczek Oracle na: http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html

 0
Author: Bjarne Boström,
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-02-25 14:11:42