Jaka jest różnica między następującymi sposobami postępowania InterruptedException
? Jak najlepiej to zrobić?
try{
//...
} catch(InterruptedException e) {
Thread.currentThread().interrupt();
}
LUB
try{
//...
} catch(InterruptedException e) {
throw new RuntimeException(e);
}
EDYCJA: Chciałbym również wiedzieć, w których scenariuszach te dwa są używane.
Odpowiedzi:
Prawdopodobnie przyszedłeś zadać to pytanie, ponieważ wywołałeś metodę rzucającą
InterruptedException
.Przede wszystkim powinieneś zobaczyć,
throws InterruptedException
co to jest: Część podpisu metody i możliwy wynik wywołania metody, którą wywołujesz. Zacznijmy od uwzględnienia faktu, że anInterruptedException
jest całkowicie poprawnym wynikiem wywołania metody.Teraz, jeśli wywoływana metoda generuje taki wyjątek, co powinna zrobić twoja metoda? Możesz znaleźć odpowiedź, myśląc o następujących kwestiach:
Czy ma sens metoda, którą wdrażasz, aby rzucić
InterruptedException
? Innymi słowy, jestInterruptedException
to rozsądny wynik podczas wywoływania swój sposób?Jeśli Tak , to
throws InterruptedException
powinno być częścią Twojego podpisu metody i należy pozwolić propagować wyjątku (czyli nie złapać go w ogóle).Jeśli nie , nie powinieneś deklarować swojej metody
throws InterruptedException
i powinieneś (musisz!) Złapać wyjątek. Teraz w tej sytuacji należy pamiętać o dwóch rzeczach:Ktoś przerwał twój wątek. Że ktoś prawdopodobnie chce anulować operację, zakończyć program z wdziękiem lub cokolwiek innego. Powinieneś być uprzejmy wobec tej osoby i powrócić ze swojej metody bez zbędnych ceregieli.
Nawet jeśli twoja metoda jest w stanie wygenerować sensowną wartość zwrotną w przypadku
InterruptedException
przerwania wątku, może nadal mieć znaczenie. W szczególności kod wywołujący metodę może być zainteresowany tym, czy wystąpiła przerwa podczas wykonywania metody. Powinieneś zatem zarejestrować fakt, że doszło do zakłócenia, ustawiając flagę przerwania:Thread.currentThread().interrupt()
Do tej pory powinno być jasne, że samo robienie
throw new RuntimeException(e)
jest złym pomysłem. Dzwoniący nie jest zbyt uprzejmy. Możesz wymyślić nowy wyjątek czasu wykonywania, ale główna przyczyna (ktoś chce, aby wątek zatrzymał wykonywanie) może zostać utracona.Inne przykłady:
Ten post został przepisany jako artykuł tutaj .
źródło
interrupt()
aby zachować status przerwania. Jaki jest powód, dla którego nie robisz tego na urządzeniuThread.sleep()
?Thread.currentThread.interrupt()
wInterruptedException
bloku catch ustawi tylko flagę przerwania. Nie spowodujeInterruptedException
to wyrzucenia / złapania innego w zewnętrzną stronęInterruptedException
bloku zaczepu.Tak się składa, że właśnie czytałem o tym dziś rano w drodze do pracy w Java Concurrency In Practice autorstwa Briana Goetza. Zasadniczo mówi, że powinieneś zrobić jedną z trzech rzeczy
Propaguj
InterruptedException
- Zadeklaruj metodę rzucenia zaznaczeniaInterruptedException
, aby rozmówca musiał sobie z tym poradzić.Przywróć przerwanie - Czasami nie możesz rzucać
InterruptedException
. W takich przypadkach powinieneś złapaćInterruptedException
i przywrócić status przerwania, wywołującinterrupt()
metodę nacurrentThread
tak, aby kod znajdujący się wyżej na stosie wywołań mógł zobaczyć, że wystąpiło przerwanie, i szybko wrócić z metody. Uwaga: ma to zastosowanie tylko wtedy, gdy twoja metoda ma semantykę „spróbuj” lub „najlepiej się wysila”, tzn. Nic krytycznego by się nie wydarzyło, gdyby metoda nie osiągnęła swojego celu. Na przykład,log()
lubsendMetric()
może być taką metodą, lubboolean tryTransferMoney()
, ale nievoid transferMoney()
. Zobacz tutaj po więcej szczegółów.Uninterruptibles
.Uninterruptibles
przejąć kod płyty wzorcowej jak w przykładzie Zadania niepodlegającego anulowaniu w JCIP § 7.1.3.źródło
Co próbujesz zrobić?
InterruptedException
Jest generowany, gdy wątek jest oczekiwanie lub spania, a kolejne przerwania nitki to stosującinterrupt
metodę w klasieThread
. Jeśli więc złapiesz ten wyjątek, oznacza to, że wątek został przerwany. Zwykle nie ma sensu dzwonićThread.currentThread().interrupt();
ponownie, chyba że chcesz sprawdzić status „przerwanego” wątku z innego miejsca.Jeśli chodzi o twoją inną opcję rzucania
RuntimeException
, nie wydaje się to zbyt mądrym posunięciem (kto to złapie? Jak to będzie zrobione?), Ale trudno powiedzieć więcej bez dodatkowych informacji.źródło
Thread.currentThread().interrupt()
ustawia flagę przerwania (ponownie), co jest przydatne, jeśli chcemy mieć pewność, że przerwanie zostanie zauważone i przetworzone na wyższym poziomie.Dla mnie kluczową rzeczą jest to: Przerwany Wyjątek nie jest niczym złym, jest to wątek robiąc to, co mu kazałeś. Dlatego ponowne włączenie go w RuntimeException nie ma sensu.
W wielu przypadkach sensowne jest ponowne zwrócenie wyjątku zawartego w RuntimeException, gdy mówisz: Nie wiem, co poszło tutaj źle i nie mogę nic zrobić, aby to naprawić, po prostu chcę, aby wyszło z bieżącego przepływu przetwarzania i naciśnij dowolny program obsługi wyjątków dla całej aplikacji, aby go zarejestrować. Nie jest tak w przypadku InterruptedException, to tylko wątek reagujący na wywołanie interrupt (), rzuca InterruptedException, aby pomóc anulować przetwarzanie wątku w odpowiednim czasie.
Dlatego rozpowszechnij wyjątek InterruptedException lub zjedz go inteligentnie (tzn. W miejscu, w którym osiągnie to, co było zamierzone) i zresetuj flagę przerwania. Zauważ, że flaga przerwania jest usuwana, gdy zostanie zgłoszony wyjątek InterruptedException; założeniem twórców bibliotek Jdk jest to, że wyłapanie wyjątku sprowadza się do jego obsługi, więc domyślnie flaga jest kasowana.
Zdecydowanie więc pierwszy sposób jest lepszy, drugi opublikowany przykład w pytaniu nie jest przydatny, chyba że nie spodziewasz się, że wątek rzeczywiście zostanie przerwany, a przerwanie go oznacza błąd.
Oto odpowiedź, którą napisałem, opisując na przykład, jak działają przerwania . Możesz zobaczyć w przykładowym kodzie, w którym używa on InterruptedException do zwolnienia z pętli while w metodzie run Runnable.
źródło
Prawidłowym domyślnym wyborem jest dodanie InterruptedException do listy rzutów. Przerwanie wskazuje, że inny wątek chce, aby Twój koniec się zakończył. Powód tego żądania nie jest oczywisty i jest całkowicie kontekstowy, więc jeśli nie posiadasz żadnej dodatkowej wiedzy, powinieneś założyć, że jest to tylko przyjazne zamknięcie, a wszystko, co pozwala uniknąć tego zamknięcia, jest nieprzyjazną odpowiedzią.
Java nie wyrzuci losowo wyjątków InterruptedException, wszystkie porady nie wpłyną na twoją aplikację, ale natknąłem się na przypadek, w którym deweloper przestrzegający strategii „połknięcia” stał się bardzo niewygodny. Zespół opracował duży zestaw testów i bardzo często korzystał z Thread. Teraz zaczęliśmy przeprowadzać testy na naszym serwerze CI, a czasem z powodu wad w kodzie utknęliśmy w ciągłym oczekiwaniu. Co gorsza, przy próbie anulowania zadania CI nigdy się nie zamykało, ponieważ Przerwanie Wątku, które miało przerwać test, nie przerywa zadania. Musieliśmy zalogować się do skrzynki 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 swojej listy rzutów, zawinię go w RuntimeException.
Istnieje bardzo racjonalny argument, że InterruptedException powinien być samym RuntimeException, ponieważ zachęcałoby to do lepszej „domyślnej” obsługi. To nie jest RuntimeException tylko dlatego, że projektanci trzymali się kategorycznej reguły, że RuntimeException powinien reprezentować błąd w kodzie. Ponieważ wyjątek InterruptedException nie powstaje bezpośrednio z błędu w kodzie, nie jest nim. Ale w rzeczywistości często pojawia się wyjątek InterruptedException, ponieważ w kodzie jest błąd (tj. Nieskończona pętla, dead-lock), a Interrupt jest metodą innego wątku, która radzi sobie z tym błędem.
Jeśli wiesz, że należy zrobić racjonalne porządki, zrób to. Jeśli znasz głębszą przyczynę Przerwania, możesz podjąć bardziej kompleksową obsługę.
Podsumowując, wybory do obsługi powinny być następujące:
źródło
Chciałem tylko dodać ostatnią opcję do tego, o czym wspomina większość ludzi i artykułów. Jak stwierdził mR_fr0g, ważne jest, aby poprawnie obsługiwać przerwanie poprzez:
Propagowanie wyjątku InterruptException
Przywróć stan przerwania w wątku
Lub dodatkowo:
Nie ma nic złego w obsłudze przerwań w niestandardowy sposób, w zależności od okoliczności. Ponieważ przerwanie jest żądaniem zakończenia, w przeciwieństwie do wymuszonego polecenia, całkowicie poprawne jest wykonanie dodatkowej pracy, aby aplikacja mogła płynnie obsłużyć żądanie. Na przykład, jeśli wątek śpi, czeka na IO lub odpowiedź sprzętową, gdy odbierze przerwanie, to jest całkowicie poprawne, aby z wdziękiem zamknąć wszystkie połączenia przed zakończeniem wątku.
Bardzo polecam zrozumienie tematu, ale ten artykuł jest dobrym źródłem informacji: http://www.ibm.com/developerworks/java/library/j-jtp05236/
źródło
Powiedziałbym, że w niektórych przypadkach nic nie rób. Prawdopodobnie nie jest to coś, co powinieneś robić domyślnie, ale w przypadku, gdy nie powinno być mowy o przerwaniu, nie jestem pewien, co jeszcze można zrobić (prawdopodobnie błąd logowania, ale to nie wpływa na przebieg programu).
Jeden przypadek dotyczy sytuacji, w której masz kolejkę zadań (blokujących). W przypadku, gdy masz wątek demona obsługujący te zadania i nie przerywasz wątku samodzielnie (według mojej wiedzy jvm nie przerywa wątków demona po zamknięciu jvm), nie widzę żadnego sposobu na przerwanie, dlatego może to być po prostu zignorowałem. (Wiem, że wątek demona może zostać zabity przez JVM w dowolnym momencie i dlatego w niektórych przypadkach jest nieodpowiedni).
EDYCJA: Inną sprawą mogą być strzeżone bloki, przynajmniej na podstawie samouczka Oracle pod adresem: http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html
źródło